// Copyright (C) 1984-1998 by Symantec
// Copyright (C) 2000-2009 by Digital Mars
// All Rights Reserved
// http://www.digitalmars.com
// Written by Walter Bright
/*
 * This source file is made available for personal use
 * only. The license is in /dmd/src/dmd/backendlicense.txt
 * For any other uses, please contact Digital Mars.
 */

#if !HTOD && (SCPP || MARS)

#include	<stdio.h>
#include	<string.h>
#include	<stdlib.h>
#include	<malloc.h>
#include	<ctype.h>

#include	"../l_service/c_ds/filespec.h"

#include	"cc.h"
#include	"global.h"
#include	"cgcv.h"
#include	"code.h"
#include	"type.h"
#include	"../l_service/c_ds/outbuf.h"

#include	"md5.h"

#if MARS
struct Loc
{
    char *filename;
    unsigned linnum;

    Loc(int x)
    {
        linnum = x;
        filename = NULL;
    }
};

void error(Loc loc, const char *format, ...);
#endif

#if OMFOBJ

static char __file__[] = __FILE__;	// for tassert.h
#include	"tassert.h"

#define MULTISCOPE	1	/* account for bug in MultiScope debugger
				   where it cannot handle a line number
				   with multiple offsets. We use a bit vector
				   to filter out the extra offsets.
				 */

#define TOOFFSET(a,b)	(I32 ? TOLONG(a,b) : TOWORD(a,b))

/**************************
 * Record types:
 */

#define RHEADR	0x6E
#define REGINT	0x70
#define REDATA	0x72
#define RIDATA	0x74
#define OVLDEF	0x76
#define ENDREC	0x78
#define BLKDEF	0x7A
#define BLKEND	0x7C
//#define DEBSYM	0x7E
#define THEADR	0x80
#define LHEADR	0x82
#define PEDATA	0x84
#define PIDATA	0x86
#define COMENT	0x88
#define MODEND	0x8A
#define EXTDEF	0x8C
#define TYPDEF	0x8E
#define PUBDEF	0x90
#define PUB386	0x91
#define LOCSYM	0x92
#define LINNUM	0x94
#define LNAMES	0x96
#define SEGDEF	0x98
#define SEG386	0x99
#define GRPDEF	0x9A
#define FIXUPP	0x9C
#define FIX386	0x9D
#define LEDATA	0xA0
#define LED386	0xA1
#define LIDATA	0xA2
#define LID386	0xA3
#define LIBHED	0xA4
#define LIBNAM	0xA6
#define LIBLOC	0xA8
#define LIBDIC	0xAA
#define COMDEF	0xB0
#define LEXTDEF	0xB4
#define LPUBDEF	0xB6
#define LCOMDEF	0xB8
#define CEXTDEF	0xBC
#define COMDAT	0xC2
#define LINSYM	0xC4
#define ALIAS	0xC6
#define LLNAMES	0xCA

// Some definitions for .OBJ files. Trial and error to determine which
// one to use when. Page #s refer to Intel spec on .OBJ files.

// Values for LOCAT byte: (pg. 71)
#define LOCATselfrel	0x8000
#define LOCATsegrel	0xC000
// OR'd with one of the following:
#define LOClobyte		0x0000
#define LOCbase			0x0800
#define LOChibyte		0x1000
#define LOCloader_resolved	0x1400

// Unfortunately, the fixup stuff is different for EASY OMF and Microsoft
#define EASY_LOCoffset		0x1400		// 32 bit offset
#define EASY_LOCpointer		0x1800		// 48 bit seg/offset

#define	LOC32offset		0x2400
#define LOC32tlsoffset		0x2800
#define LOC32pointer		0x2C00

#define LOC16offset		0x0400
#define LOC16pointer		0x0C00

#define LOCxx			0x3C00

// FDxxxx are constants for the FIXDAT byte in fixup records (pg. 72)

#define FD_F0	0x00		// segment index
#define FD_F1	0x10		// group index
#define FD_F2	0x20		// external index
#define FD_F4	0x40		// canonic frame of LSEG that contains Location
#define FD_F5	0x50		// Target determines the frame

#define FD_T0	0		// segment index
#define FD_T1	1		// group index
#define FD_T2	2		// external index
#define FD_T4	4		// segment index, 0 displacement
#define FD_T5	5		// group index, 0 displacement
#define FD_T6	6		// external index, 0 displacement

/***************
 * Fixup list.
 */

struct FIXUP
{
    struct FIXUP	*FUnext;
    targ_size_t		FUoffset;	// offset from start of ledata
    unsigned short	FUlcfd;		// LCxxxx | FDxxxx
    unsigned short	FUframedatum;
    unsigned short	FUtargetdatum;
};

#define list_fixup(fl)	((struct FIXUP *)list_ptr(fl))

#define seg_is_comdat(seg) ((seg) < 0)

/*****************************
 * Ledata records
 */

#define LEDATAMAX (1024-14)

struct Ledatarec
{
    char header[14];			// big enough to handle COMDAT header
    char data[LEDATAMAX];
    int lseg;				// segment value
    unsigned i;				// number of bytes in data
    targ_size_t offset;			// segment offset of start of data
    struct FIXUP *fixuplist;		// fixups for this ledata

    // For COMDATs
    unsigned char flags;		// flags byte of COMDAT
    unsigned char alloctyp;		// allocation type of COMDAT
    unsigned char align;		// align type
    int typidx;
    int pubbase;
    int pubnamidx;
};

#define list_ledata(dl)	((Ledatarec *)list_ptr(dl))

/*****************************
 * For defining segments.
 */

#define SEG_ATTR(A,C,B,P)	(((A) << 5) | ((C) << 2) | ((B) << 1) | (P))

// Segment alignment A
#define SEG_ALIGN0	0	// absolute segment
#define SEG_ALIGN1	1	// byte align
#define SEG_ALIGN2	2	// word align
#define SEG_ALIGN16	3	// paragraph align
#define SEG_ALIGN4K	4	// 4Kb page align
#define SEG_ALIGN4	5	// dword align

// Segment combine types C
#define SEG_C_ABS	0
#define SEG_C_PUBLIC	2
#define SEG_C_STACK	5
#define	SEG_C_COMMON	6

// Segment type P
#define USE16	0
#define USE32	1

#define USE32_CODE	(4+2)		// use32 + execute/read
#define USE32_DATA	(4+3)		// use32 + read/write

/**********************
 * Struct to keep track of far segments and comdats.
 */

#if 0
typedef struct Farseg
{
    bool isfarseg;
    int seg;				// segment number
    int lnameidx;			// lname idx of segment name
    int classidx;			// lname idx of class name
    unsigned attr;			// segment attribute
    targ_size_t origsize;		// original size
    targ_size_t SDoffset;		// current size
    long seek;				// seek position in output file
} Farseg;
#endif

/*****************************
 * Line number support.
 */

#define LINNUMMAX	512

struct Linnum
{
#if MARS
	char *filename;		// source file name
#else
	Sfile *filptr;		// file pointer
#endif
	int cseg;		// our internal segment number
	int seg;		// segment/public index
	int i;			// used in data[]
	char data[LINNUMMAX];	// linnum/offset data
};

#define LINRECMAX	(2 + 255 * 2)	// room for 255 line numbers

/************************************
 * State of object file.
 */

struct Objstate
{
    const char *modname;
    char *csegname;
    Outbuffer *buf;	// output buffer

    int fdsegattr;	// far data segment attribute
    int csegattr;	// code segment attribute

    targ_size_t codeSegOffset;	// size of default code segment

    int farsegi;		// how many in farseg[]
    int lastfarcodesegi;	// index of last far code seg
    int lastfardatasegi;	// index of last far data seg

    int LOCoffset;
    int LOCpointer;

    int mlidata;
    int mpubdef;
    int mfixupp;
    int mmodend;

    int lnameidx;		// index of next LNAMES record
    int segidx;			// index of next SEGDEF record
    int extidx;			// index of next EXTDEF record
    int pubnamidx;		// index of COMDAT public name index

    Symbol *startaddress;	// if !NULL, then Symbol is start address

#ifdef DEBUG
    int fixup_count;
#endif

    Ledatarec *ledata;
    list_t ledata_list;		// list of Ledatarec blocks

    // Line numbers
    list_t linnum_list;
    char *linrec;		// line number record
    unsigned linreci;		// index of next avail in linrec[]
    unsigned linrecheader;	// size of line record header
    unsigned linrecnum;		// number of line record entries
    list_t linreclist;		// list of line records
    int mlinnum;
    int recseg;
    int term;
#if MULTISCOPE
    vec_t linvec;		// bit vector of line numbers used
    vec_t offvec;		// and offsets used
#endif

    int fiseg;			// segment number of FI segment
    int fisegi;			// index into farseg[] of FI segment

#if MARS
    int fmseg;			// segment number of FM segment
    int fmsegi;			// index into farseg[] of FM segment
#endif

    int tlsseg;			// TLS segment
    int tlssegi;		// index into farseg[] of tls segment
    int fardataidx;

    char pubdata[1024];
    int pubdatai;

    char extdata[1024];
    int extdatai;

    // For obj_far16thunk
    int code16segi;
    int code16;
    targ_size_t CODE16offset;

    int fltused;
    int nullext;
};

seg_data **SegData;
static int seg_count;
static int seg_max;

targ_size_t	Coffset;	/* size of current code segment		*/

static Objstate obj;

STATIC void obj_defaultlib();
STATIC void objheader (char *csegname);
STATIC char * objmodtoseg (const char *modname);
STATIC void obj_browse_flush();
STATIC int obj_newfarseg (targ_size_t size,int);
STATIC void linnum_flush(void);
STATIC void linnum_term(void);
STATIC void objsegdef (int attr,targ_size_t size,int segnamidx,int classnamidx);
STATIC void obj_modend();
STATIC void objfixupp (struct FIXUP *);
STATIC void outextdata();
STATIC void outpubdata();
STATIC void ledata_new (int seg,targ_size_t offset);

char *id_compress(char *id, int idlen);

/*******************************
 * Output an object file data record.
 * Input:
 *	rectyp	=	record type
 *	record	->	the data
 *	reclen	=	# of bytes in record
 */

void objrecord(unsigned rectyp,const char *record,unsigned reclen)
{   Outbuffer *o = obj.buf;

    //printf("rectyp = x%x, record[0] = x%x, reclen = x%x\n",rectyp,record[0],reclen);
    o->reserve(reclen + 4);
    o->writeByten(rectyp);
    o->writeWordn(reclen + 1);	// record length includes checksum
    o->writen(record,reclen);
    o->writeByten(0);		// use 0 for checksum
}


/**************************
 * Insert an index number.
 * Input:
 *	p -> where to put the 1 or 2 byte index
 *	index = the 15 bit index
 * Returns:
 *	# of bytes stored
 */

#if !DEBUG && TX86 && __INTSIZE == 4 && !defined(_MSC_VER)
__declspec(naked) int __pascal insidx(char *p,unsigned index)
{
#undef AL
#undef AH
#undef DL
#undef DH
    _asm
    {
	mov	EAX,index - 4[ESP]
	mov	ECX,p - 4[ESP]
	cmp	EAX,0x7F
	jae	L1
	mov	[ECX],AL
	mov	EAX,1
	ret	8


    L1:
	cmp	EAX,0x7FFF
	ja	L2

	mov	1[ECX],AL
	or	EAX,0x8000
	mov	[ECX],AH
	mov	EAX,2
	ret	8
    }
    L2:
	assert(0);
}
#else
__inline int insidx(char *p,unsigned index)
{
    //if (index > 0x7FFF) printf("index = x%x\n",index);
    if (index <= 127)
    {	*p = index;
	return 1;
    }
    else
    {	assert(index <= 0x7FFF);
	*(p + 1) = index;
	*p = (index >> 8) | 0x80;
	return 2;
    }
}
#endif

/**************************
 * Insert a type index number.
 * Input:
 *	p -> where to put the 1 or 2 byte index
 *	index = the 15 bit index
 * Returns:
 *	# of bytes stored
 */

__inline int instypidx(char *p,unsigned index)
{
    if (index <= 127)
    {	*p = index;
	return 1;
    }
    else if (index <= 0x7FFF)
    {	*(p + 1) = index;
	*p = (index >> 8) | 0x80;
	return 2;
    }
    else			// overflow
    {	*p = 0;			// the linker ignores this field anyway
	return 1;
    }
}

/****************************
 * Read index.
 */

#define getindex(p) ((*(p) & 0x80) \
    ? ((*(unsigned char *)(p) & 0x7F) << 8) | *((unsigned char *)(p) + 1) \
    : *(unsigned char *)(p))

/*****************************
 * Returns:
 *	# of bytes stored
 */

#define ONS_OHD	4		// max # of extra bytes added by obj_namestring()

STATIC int obj_namestring(char *p,const char *name)
{   unsigned len;

    len = strlen(name);
    if (len > 255)
    {	p[0] = 0xFF;
	p[1] = 0;
#ifdef DEBUG
	assert(len <= 0xFFFF);
#endif
	TOWORD(p + 2,len);
	memcpy(p + 4,name,len);
	len += ONS_OHD;
    }
    else
    {	p[0] = len;
	memcpy(p + 1,name,len);
	len++;
    }
    return len;
}

/******************************
 * Allocate a new segment.
 * Return index for the new segment.
 */

seg_data *getsegment()
{
    int seg = ++seg_count;
    if (seg_count == seg_max)
    {
        seg_max += 10;
        SegData = (seg_data **)mem_realloc(SegData, seg_max * sizeof(seg_data));
        memset(&SegData[seg_count], 0, 10 * sizeof(seg_data *));
    }
    assert(seg_count < seg_max);
    if (SegData[seg])
	memset(SegData[seg], 0, sizeof(seg_data));
    else
	SegData[seg] = (seg_data *)mem_calloc(sizeof(seg_data));

    seg_data *pseg = SegData[seg];
    pseg->SDseg = seg;
    return pseg;
}


/******************************
 * Perform initialization that applies to all .obj output files.
 * Input:
 *	filename	source file name
 *	csegname	code segment name (can be NULL)
 */

void obj_init(Outbuffer *objbuf, const char *filename, const char *csegname)
{
	memset(&obj,0,sizeof(obj));

	obj.buf = objbuf;
	obj.buf->reserve(40000);

	obj.tlsseg = UNKNOWN;

	obj.lastfarcodesegi = -1;
	obj.lastfardatasegi = -1;

	obj.mlidata = LIDATA;
	obj.mpubdef = PUBDEF;
	obj.mfixupp = FIXUPP;
	obj.mmodend = MODEND;
	obj.mlinnum = LINNUM;


	// Reset for different OBJ file formats
	if (I32)
	{   if (config.flags & CFGeasyomf)
	    {	obj.LOCoffset = EASY_LOCoffset;
		obj.LOCpointer = EASY_LOCpointer;
	    }
	    else
	    {
		obj.mlidata = LID386;
		obj.mpubdef = PUB386;
		obj.mfixupp = FIX386;
		obj.mmodend = MODEND + 1;
		obj.LOCoffset = LOC32offset;
		obj.LOCpointer = LOC32pointer;
	    }
	    obj.fdsegattr = SEG_ATTR(SEG_ALIGN16,SEG_C_PUBLIC,0,USE32);
	    obj.csegattr  = SEG_ATTR(SEG_ALIGN4, SEG_C_PUBLIC,0,USE32);
	}
	else
	{
	    obj.LOCoffset  = LOC16offset;
	    obj.LOCpointer = LOC16pointer;
	    obj.fdsegattr = SEG_ATTR(SEG_ALIGN16,SEG_C_PUBLIC,0,USE16);
	    obj.csegattr  = SEG_ATTR(SEG_ALIGN2, SEG_C_PUBLIC,0,USE16);
	}

	if (config.flags4 & CFG4speed && // if optimized for speed
	    config.target_cpu == TARGET_80486)
	    // 486 is only CPU that really benefits from alignment
	    obj.csegattr  = I32 ? SEG_ATTR(SEG_ALIGN16, SEG_C_PUBLIC,0,USE32)
			        : SEG_ATTR(SEG_ALIGN16, SEG_C_PUBLIC,0,USE16);

	if (!SegData)
	{   seg_max = UDATA + 10;
	    SegData = (seg_data **)mem_calloc(seg_max * sizeof(seg_data *));
	}

	for (int i = 0; i < seg_max; i++)
	{
	    if (SegData[i])
		memset(SegData[i], 0, sizeof(seg_data));
	    else
		SegData[i] = (seg_data *)mem_calloc(sizeof(seg_data));
	}

	SegData[CODE]->SDseg = CODE;
	SegData[DATA]->SDseg = DATA;
	SegData[CDATA]->SDseg = CDATA;
	SegData[UDATA]->SDseg = UDATA;
	seg_count = UDATA;

	if (config.fulltypes)
	{
	    SegData[DEBSYM]->SDseg = DEBSYM;
	    SegData[DEBTYP]->SDseg = DEBTYP;
	    seg_count = DEBTYP;
	}

	obj_theadr(filename);
	obj.modname = filename;
	if (!csegname || !*csegname)		// if no code seg name supplied
	    obj.csegname = objmodtoseg(obj.modname);	// generate one
	else
	    obj.csegname = mem_strdup(csegname);	// our own copy
 	objheader(obj.csegname);
  	objseggrp(0,0,0,0);		// obj seg and grp info
	ledata_new(cseg,0);		// so ledata is never NULL
	if (config.fulltypes)		// if full typing information
	    cv_init();			// initialize debug output code
}

/**************************
 * Initialize the start of object output for this particular .obj file.
 */

void obj_initfile(const char *filename,const char *csegname, const char *modname)
{
}

/***************************
 * Fixup and terminate object file.
 */

void obj_termfile()
{
}

/*********************************
 * Terminate package.
 */

void obj_term()
{
	//printf("obj_term()\n");
	int i;
	list_t dl;
	unsigned long size;

#if SCPP
	if (!errcnt)
#endif
	{   obj_defaultlib();
	    outfixlist();		// backpatches
	}

	if (config.fulltypes)
	    cv_term();			// write out final debug info
	outextdata();			// finish writing EXTDEFs
	outpubdata();			// finish writing PUBDEFs

	// Put out LEDATA records and associated fixups
	obj.ledata_list = list_reverse(obj.ledata_list);
	for (dl = obj.ledata_list; dl; dl = list_next(dl))
	{   Ledatarec *d = list_ledata(dl);

	    if (d->i)			// if any data in this record
	    {	// Fill in header
		int headersize;
		int rectyp;
		int lseg = d->lseg;
		char header[sizeof(d->header)];

		if (lseg < 0)	// if COMDAT
		{
		    header[0] = d->flags | (d->offset ? 1 : 0);	// continuation flag
		    header[1] = d->alloctyp;
		    header[2] = d->align;
		    TOOFFSET(header + 3,d->offset);
		    headersize = 3 + intsize;
		    headersize += instypidx(header + headersize,d->typidx);
		    if ((header[1] & 0x0F) == 0)
		    {   // Group index
			header[headersize] = (d->pubbase == DATA) ? 1 : 0;
			headersize++;

			// Segment index
			headersize += insidx(header + headersize,d->pubbase);
		    }
		    headersize += insidx(header + headersize,d->pubnamidx);

		    rectyp = I32 ? COMDAT + 1 : COMDAT;
		}
		else
		{
		    rectyp = LEDATA;
		    headersize = insidx(header,lseg);
		    if (intsize == LONGSIZE || d->offset & ~0xFFFFL)
		    {	if (!(config.flags & CFGeasyomf))
			    rectyp++;
			TOLONG(header + headersize,d->offset);
			headersize += 4;
		    }
		    else
		    {
			TOWORD(header + headersize,d->offset);
			headersize += 2;
		    }
		}
		assert(headersize <= sizeof(d->header));

		// Right-justify data in d->header[]
		memcpy(d->header + sizeof(d->header) - headersize,header,headersize);
		//printf("objrecord(rectyp=x%02x, d=%p, p=%p, size = %d)\n",
		//rectyp,d,d->header + (sizeof(d->header) - headersize),d->i + headersize);

		objrecord(rectyp,d->header + (sizeof(d->header) - headersize),
			d->i + headersize);
		objfixupp(d->fixuplist);
	    }
	}
#if TERMCODE
	list_free(&obj.ledata_list,mem_freefp);
#endif

	linnum_term();
	obj_modend();

	size = obj.buf->size();
  	obj.buf->setsize(0);		// rewind file
	obj_theadr(obj.modname);
  	objheader(obj.csegname);
	mem_free(obj.csegname);
	if (obj.lastfarcodesegi != -1)
	{
	    SegData[obj.lastfarcodesegi]->SDoffset = Coffset;
	    Coffset = obj.codeSegOffset;		// reset for benefit of next line
	}
  	objseggrp(Coffset,Doffset,0,UDoffset);	// do real sizes

	// Update any out-of-date far segment sizes
	for (i = 0; i <= seg_count; i++)
        {   seg_data *f = SegData[i];
            if (f->isfarseg && f->origsize != f->SDoffset)
            {	obj.buf->setsize(f->seek);
		objsegdef(f->attr,f->SDoffset,f->lnameidx,f->classidx);
	    }
        }
	//mem_free(obj.farseg);

#ifdef DEBUG
	printf("Max # of fixups = %d\n",obj.fixup_count);
#endif

	obj.buf->setsize(size);
}

/*****************************
 * Line number support.
 */

/***************************
 * Record line number linnum at offset.
 * Input:
 *	cseg	current code segment (negative for COMDAT segments)
 *	pubnamidx
 *	obj.mlinnum		LINNUM or LINSYM
 */

void objlinnum(Srcpos srcpos,targ_size_t offset)
{
    unsigned linnum = srcpos.Slinnum;

#if 0
#if MARS
    printf("objlinnum(cseg=%d, filename=%s linnum=%u, offset=x%lx)\n",
	cseg,srcpos.Sfilename ? srcpos.Sfilename : "null",linnum,offset);
#else
    printf("objlinnum(cseg=%d, filptr=%p linnum=%u, offset=x%lx)\n",
	cseg,srcpos.Sfilptr ? *srcpos.Sfilptr : 0,linnum,offset);
    if (srcpos.Sfilptr)
    {
	Sfile *sf = *srcpos.Sfilptr;
	printf("filename = %s\n", sf ? sf->SFname : "null");
    }
#endif
#endif
    char linos2 = config.exe == EX_OS2 && cseg >= 0;

#if MARS
    if (!obj.term &&
	(cseg < 0 || (srcpos.Sfilename && srcpos.Sfilename != obj.modname)))
#else
    if (!srcpos.Sfilptr)
	return;
    sfile_debug(&srcpos_sfile(srcpos));
    if (!obj.term && (!(srcpos_sfile(srcpos).SFflags & SFtop) || (cseg < 0 && !obj.term)))
#endif
    {	// Not original source file, or a COMDAT.
	// Save data away and deal with it at close of compile.
	// It is done this way because presumably 99% of the lines
	// will be in the original source file, so we wish to minimize
	// memory consumption and maximize speed.
	list_t ll;
	struct Linnum *ln;

	if (linos2)
	    return;		// BUG: not supported under OS/2
	for (ll = obj.linnum_list; 1; ll = list_next(ll))
	{
	    if (!ll)
	    {
		ln = (struct Linnum *) mem_calloc(sizeof(struct Linnum));
#if MARS
		ln->filename = srcpos.Sfilename;
#else
		ln->filptr = *srcpos.Sfilptr;
#endif
		ln->cseg = cseg;
		ln->seg = obj.pubnamidx;
		list_prepend(&obj.linnum_list,ln);
		break;
	    }
	    ln = (Linnum *)list_ptr(ll);
	    if (
#if MARS
		(ln->filename == srcpos.Sfilename) &&
#endif
#if SCPP
		(ln->filptr == *srcpos.Sfilptr) &&
#endif
		ln->cseg == cseg &&
		ln->i < LINNUMMAX - 6)
		break;
	}
	TOWORD(&ln->data[ln->i],linnum);
	TOOFFSET(&ln->data[ln->i + 2],offset);
	ln->i += 2 + intsize;
    }
    else
    {
	if (linos2 && obj.linreci > LINRECMAX - 8)
	    obj.linrec = NULL;			// allocate a new one
	else if (cseg != obj.recseg)
	    linnum_flush();

	if (!obj.linrec)			// if not allocated
	{	obj.linrec = (char *) mem_calloc(LINRECMAX);
		obj.linrec[0] = 0;		// base group / flags
		obj.linrecheader = 1 + insidx(obj.linrec + 1,cseg < 0 ? obj.pubnamidx : cseg);
		obj.linreci = obj.linrecheader;
		obj.recseg = cseg;
#if MULTISCOPE
		if (!obj.linvec)
		{   obj.linvec = vec_calloc(1000);
		    obj.offvec = vec_calloc(1000);
		}
#endif
		if (linos2)
		{
		    if (!obj.linreclist)	// if first line number record
			obj.linreci += 8;	// leave room for header
		    list_append(&obj.linreclist,obj.linrec);
		}

		// Select record type to use
		obj.mlinnum = (cseg < 0) ? LINSYM : LINNUM;
		if (I32 && !(config.flags & CFGeasyomf))
		    obj.mlinnum++;
	}
	else if (obj.linreci > LINRECMAX - (2 + intsize))
	{	objrecord(obj.mlinnum,obj.linrec,obj.linreci);	// output data
		obj.linreci = obj.linrecheader;
		if (cseg < 0)			// if LINSYM record
		    obj.linrec[0] |= 1;		// continuation bit
	}
#if MULTISCOPE
	if (linnum >= vec_numbits(obj.linvec))
	    obj.linvec = vec_realloc(obj.linvec,linnum + 1000);
	if (offset >= vec_numbits(obj.offvec))
	{
#if __INTSIZE == 2
	    unsigned newsize = (unsigned)offset * 2;

	    if (offset >= 0x8000)
	    {	newsize = 0xFF00;
		assert(offset < newsize);
	    }
	    if (newsize != vec_numbits(obj.offvec))
		obj.offvec = vec_realloc(obj.offvec,newsize);
#else
	    if (offset < 0xFF00)	// otherwise we overflow ph_malloc()
		obj.offvec = vec_realloc(obj.offvec,offset * 2);
#endif
	}
	if (
#if 1 // disallow multiple offsets per line
	    !vec_testbit(linnum,obj.linvec) &&	// if linnum not already used
#endif
	    // disallow multiple lines per offset
	    (offset >= 0xFF00 || !vec_testbit(offset,obj.offvec)))	// and offset not already used
#endif
	{
#if MULTISCOPE
	    vec_setbit(linnum,obj.linvec);		// mark linnum as used
	    if (offset < 0xFF00)
		vec_setbit(offset,obj.offvec);	// mark offset as used
#endif
	    TOWORD(obj.linrec + obj.linreci,linnum);
	    if (linos2)
	    {	obj.linrec[obj.linreci + 2] = 1;	// source file index
		TOLONG(obj.linrec + obj.linreci + 4,offset);
		obj.linrecnum++;
		obj.linreci += 8;
	    }
	    else
	    {
		TOOFFSET(obj.linrec + obj.linreci + 2,offset);
		obj.linreci += 2 + intsize;
	    }
	}
    }
}

/***************************
 * Flush any pending line number records.
 */

STATIC void linnum_flush()
{
    if (obj.linreclist)
    {	list_t list;
	size_t len;

	obj.linrec = (char *) list_ptr(obj.linreclist);
	TOWORD(obj.linrec + 6,obj.linrecnum);
	list = obj.linreclist;
	while (1)
	{   obj.linrec = (char *) list_ptr(list);

	    list = list_next(list);
	    if (list)
	    {	objrecord(obj.mlinnum,obj.linrec,LINRECMAX);
		mem_free(obj.linrec);
	    }
	    else
	    {	objrecord(obj.mlinnum,obj.linrec,obj.linreci);
		break;
	    }
	}
	list_free(&obj.linreclist,FPNULL);

	// Put out File Names Table
	TOLONG(obj.linrec + 2,0);		// record no. of start of source (???)
	TOLONG(obj.linrec + 6,obj.linrecnum);	// number of primary source records
	TOLONG(obj.linrec + 10,1);		// number of source and listing files
	len = obj_namestring(obj.linrec + 14,obj.modname);
	assert(14 + len <= LINRECMAX);
	objrecord(obj.mlinnum,obj.linrec,14 + len);

	mem_free(obj.linrec);
	obj.linrec = NULL;
    }
    else if (obj.linrec)			// if some line numbers to send
    {	objrecord(obj.mlinnum,obj.linrec,obj.linreci);
	mem_free(obj.linrec);
	obj.linrec = NULL;
    }
#if MULTISCOPE
    vec_clear(obj.linvec);
    vec_clear(obj.offvec);
#endif
}

/*************************************
 * Terminate line numbers.
 */

STATIC void linnum_term()
{   list_t ll;
#if SCPP
    Sfile *lastfilptr = NULL;
#endif
#if MARS
    char *lastfilename = NULL;
#endif
    int csegsave = cseg;

    linnum_flush();
    obj.term = 1;
    while (obj.linnum_list)
    {	struct Linnum *ln;
	unsigned u;
	Srcpos srcpos;
	targ_size_t offset;

	ll = obj.linnum_list;
	ln = (struct Linnum *) list_ptr(ll);
#if SCPP
	Sfile *filptr = ln->filptr;
	if (filptr != lastfilptr)
	{   obj_theadr(filptr->SFname);
	    lastfilptr = filptr;
	}
#endif
#if MARS
	char *filename = ln->filename;
	if (filename != lastfilename)
	{
	    if (filename)
		obj_theadr(filename);
	    lastfilename = filename;
	}
#endif
	while (1)
	{
	    cseg = ln->cseg;
	    obj.pubnamidx = ln->seg;
#if MARS
	    srcpos.Sfilename = ln->filename;
#else
	    srcpos.Sfilptr = &ln->filptr;
#endif
	    for (u = 0; u < ln->i; )
	    {
		srcpos.Slinnum = *(unsigned short *)&ln->data[u];
		u += 2;
		if (I32)
		    offset = *(unsigned long *)&ln->data[u];
		else
		    offset = *(unsigned short *)&ln->data[u];
		objlinnum(srcpos,offset);
		u += intsize;
	    }
	    linnum_flush();
	    ll = list_next(ll);
	    list_subtract(&obj.linnum_list,ln);
	    mem_free(ln);
	L1:
	    if (!ll)
		break;
	    ln = (struct Linnum *) list_ptr(ll);
#if SCPP
	    if (filptr != ln->filptr)
#else
	    if (filename != ln->filename)
#endif
	    {	ll = list_next(ll);
		goto L1;
	    }
	}
    }
    cseg = csegsave;
#if MULTISCOPE
    vec_free(obj.linvec);
    vec_free(obj.offvec);
#endif
}

/*******************************
 * Set start address
 */

void obj_startaddress(Symbol *s)
{
    obj.startaddress = s;
}

/*******************************
 * Output DOSSEG coment record.
 */

void obj_dosseg()
{   static const char dosseg[] = { 0x80,0x9E };

    objrecord(COMENT,dosseg,sizeof(dosseg));
}

/*******************************
 * Embed comment record.
 */

STATIC void obj_comment(unsigned char x, const char *string, size_t len)
{
    char __ss *library;

    library = (char __ss *) alloca(2 + len);
    library[0] = 0;
    library[1] = x;
    memcpy(library + 2,string,len);
    objrecord(COMENT,library,len + 2);
}

/*******************************
 * Output library name.
 * Output:
 *	name is modified
 */

void obj_includelib(const char *name)
{   const char *p;
    size_t len = strlen(name);

    p = filespecdotext(name);
    if (!filespeccmp(p,".lib"))
	len -= strlen(p);		// lop off .LIB extension
    obj_comment(0x9F, name, len);
}

/**************************
 * Embed string in executable.
 */

void obj_exestr(const char *p)
{
    obj_comment(0xA4,p, strlen(p));
}

/**************************
 * Embed string in obj.
 */

void obj_user(const char *p)
{
    obj_comment(0xDF,p, strlen(p));
}

/*********************************
 * Put out default library name.
 */

STATIC void obj_defaultlib()
{
    char library[4];		// default library
    static const char model[MEMMODELS+1] = "SMCLV";

#if MARS
    memcpy(library,"SM?",4);
#else
    memcpy(library,"SD?",4);
#endif
    switch (config.exe)
    {
	case EX_OS2:
	    library[2] = 'F';
	case EX_OS1:
	    library[1] = 'O';
	    break;
	case EX_NT:
#if MARS
	    library[1] = 'M';
#else
	    library[1] = 'N';
#endif
	    library[2] = (config.flags4 & CFG4dllrtl) ? 'D' : 'N';
	    break;
	case EX_DOSX:
	case EX_PHARLAP:
	    library[2] = 'X';
	    break;
	default:
	    library[2] = model[config.memmodel];
	    if (config.wflags & WFwindows)
		library[1] = 'W';
	    break;
    }

    if (!(config.flags2 & CFG2nodeflib))
    {
	obj_includelib(configv.deflibname ? configv.deflibname : library);
    }
}

/*******************************
 * Output a weak extern record.
 * s1 is the weak extern, s2 is its default resolution.
 */

void obj_wkext(Symbol *s1,Symbol *s2)
{   char buffer[2+2+2];
    int i;
    int x2;

    printf("obj_wkext(%s)\n", s1->Sident);
    if (s2)
	x2 = s2->Sxtrnnum;
    else
    {
	if (!obj.nullext)
	{
	    obj.nullext = objextdef("__nullext");
	}
	x2 = obj.nullext;
    }
    outextdata();
    buffer[0] = 0x80;
    buffer[1] = 0xA8;
    i = 2;
    i += insidx(&buffer[2],s1->Sxtrnnum);
    i += insidx(&buffer[i],x2);
    objrecord(COMENT,buffer,i);
}

/*******************************
 * Output a lazy extern record.
 * s1 is the lazy extern, s2 is its default resolution.
 */

void obj_lzext(Symbol *s1,Symbol *s2)
{   char buffer[2+2+2];
    int i;

    outextdata();
    buffer[0] = 0x80;
    buffer[1] = 0xA9;
    i = 2;
    i += insidx(&buffer[2],s1->Sxtrnnum);
    i += insidx(&buffer[i],s2->Sxtrnnum);
    objrecord(COMENT,buffer,i);
}

/*******************************
 * Output an alias definition record.
 */

void obj_alias(const char *n1,const char *n2)
{   unsigned len;
    char *buffer;

    buffer = (char *) alloca(strlen(n1) + strlen(n2) + 2 * ONS_OHD);
    len = obj_namestring(buffer,n1);
    len += obj_namestring(buffer + len,n2);
    objrecord(ALIAS,buffer,len);
}

/*******************************
 * Output module name record.
 */

void obj_theadr(const char *modname)
{   char *theadr;
    int i;

    //printf("obj_theadr(%s)\n", modname);
    theadr = (char *)alloca(ONS_OHD + strlen(modname));
    i = obj_namestring(theadr,modname);
    objrecord(THEADR,theadr,i);    		// module name record
}

/*******************************
 * Embed compiler version in .obj file.
 */

void obj_compiler()
{
    static const char compiler[] = "\0\xDB Digital Mars C/C++"
	VERSION
	;	// compiled by ...

    objrecord(COMENT,compiler,sizeof(compiler) - 1);
}

/*******************************
 * Output header stuff for object files.
 * Input:
 *	csegname	Name to use for code segment (NULL if use default)
 */

STATIC void objheader(char *csegname)
{
  char *nam;
  static char lnames[] =
	"\0\06DGROUP\05_TEXT\04CODE\05_DATA\04DATA\05CONST\04_BSS\03BSS\
\07$$TYPES\06DEBTYP\011$$SYMBOLS\06DEBSYM";

#define DATACLASS	6			// data class lname index
#define BSSCLASS	9			// BSS class lname index

  // Include debug segment names if inserting type information
  int lnamesize = config.fulltypes ? sizeof(lnames) - 1 : sizeof(lnames) - 1 - 32;
  int texti = 8;				// index of _TEXT

  static char comment[] = {0,0x9D,'0','?','O'};	// memory model
  static char model[MEMMODELS+1] = "smclv";
  static char exten[] = {0,0xA1,1,'C','V'};	// extended format
  static char pmdeb[] = {0x80,0xA1,1,'H','L','L',0};	// IBM PM debug format

    if (I32)
    {	if (config.flags & CFGeasyomf)
	{
	    // Indicate we're in EASY OMF (hah!) format
	    static const char easy_omf[] = { 0x80,0xAA,'8','0','3','8','6' };
	    objrecord(COMENT,easy_omf,sizeof(easy_omf));
	}
    }

  // Send out a comment record showing what memory model was used
  comment[2] = config.target_cpu + '0';
  comment[3] = model[config.memmodel];
  if (I32)
  {	if (config.exe == EX_NT)
	    comment[3] = 'n';
	else if (config.exe == EX_OS2)
	    comment[3] = 'f';
	else
	    comment[3] = 'x';
  }
  objrecord(COMENT,comment,sizeof(comment));

    // Send out comment indicating we're using extensions to .OBJ format
    if (config.exe == EX_OS2)
	objrecord(COMENT,pmdeb,sizeof(pmdeb));
    else
	objrecord(COMENT,exten,sizeof(exten));

    // Change DGROUP to FLAT if we are doing flat memory model
    // (Watch out, objheader() is called twice!)
    if (config.exe & EX_flat)
    {
	if (lnames[2] != 'F')			// do not do this twice
	{   memcpy(lnames + 1,"\04FLAT",5);
	    memmove(lnames + 6,lnames + 8,sizeof(lnames) - 8);
	}
	lnamesize -= 2;
	texti -= 2;
    }

    // Put out segment and group names
    if (csegname)
    {	char *p;
	size_t i;

	// Replace the module name _TEXT with the new code segment name
	i = strlen(csegname);
	p = (char *)alloca(lnamesize + i - 5);
	memcpy(p,lnames,8);
	p[texti] = i;
	texti++;
	memcpy(p + texti,csegname,i);
	memcpy(p + texti + i,lnames + texti + 5,lnamesize - (texti + 5));
	objrecord(LNAMES,p,lnamesize + i - 5);
    }
    else
	objrecord(LNAMES,lnames,lnamesize);
}

/********************************
 * Convert module name to code segment name.
 * Output:
 *	mem_malloc'd code seg name
 */

STATIC char * objmodtoseg(const char *modname)
{   char *csegname = NULL;

    if (LARGECODE)		// if need to add in module name
    {	int i;
	char *m;
	static const char suffix[] = "_TEXT";

	// Prepend the module name to the beginning of the _TEXT
	m = filespecgetroot(filespecname(modname));
	strupr(m);
	i = strlen(m);
	csegname = (char *)mem_malloc(i + sizeof(suffix));
	strcpy(csegname,m);
	strcat(csegname,suffix);
	mem_free(m);
    }
    return csegname;
}

/*********************************
 * Put out a segment definition.
 */

STATIC void objsegdef(int attr,targ_size_t size,int segnamidx,int classnamidx)
{
    unsigned reclen;
    char sd[1+4+2+2+2+1];

//    printf("objsegdef(attr=x%x, size=x%x, segnamidx=x%x, classnamidx=x%x)\n",
//	attr,size,segnamidx,classnamidx);
    sd[0] = attr;
    if (attr & 1 || config.flags & CFGeasyomf)
    {	TOLONG(sd + 1,size);		// store segment size
	reclen = 5;
    }
    else
    {
#ifdef DEBUG
	assert(size <= 0xFFFF);
#endif
	TOWORD(sd + 1,size);
	reclen = 3;
    }
    reclen += insidx(sd + reclen,segnamidx);	// segment name index
    reclen += insidx(sd + reclen,classnamidx);	// class name index
    sd[reclen] = 1;				// overlay name index
    reclen++;
    if (attr & 1)			// if USE32
    {
	if (config.flags & CFGeasyomf)
	{   // Translate to Pharlap format
	    sd[0] &= ~1;		// turn off P bit

	    // Translate A: 4->6
	    attr &= SEG_ATTR(7,0,0,0);
	    if (attr == SEG_ATTR(4,0,0,0))
		sd[0] ^= SEG_ATTR(4 ^ 6,0,0,0);

	    // 2 is execute/read
	    // 3 is read/write
	    // 4 is use32
	    sd[reclen] = (classnamidx == 4) ? (4+2) : (4+3);
	    reclen++;
	}
    }
    else				// 16 bit segment
    {
#if MARS
	assert(0);
#else
	if (size & ~0xFFFFL)
	{   if (size == 0x10000)	// if exactly 64Kb
		sd[0] |= 2;		// set "B" bit
	    else
		synerr(EM_seg_gt_64k,size);	// segment exceeds 64Kb
	}
#endif
    }
#ifdef DEBUG
    assert(reclen <= sizeof(sd));
#endif
    objrecord(SEGDEF + (sd[0] & 1),sd,reclen);
}

/*********************************
 * Output segment and group definitions.
 * Input:
 *	codesize	size of code segment
 *	datasize	size of initialized data segment
 *	cdatasize	size of initialized const data segment
 *	udatasize	size of uninitialized data segment
 */

void objseggrp(targ_size_t codesize,targ_size_t datasize,
		targ_size_t cdatasize,targ_size_t udatasize)
{
    int dsegattr;
    int dsymattr;

    // Group into DGROUP the segments CONST, _BSS and _DATA
    // For FLAT model, it's just GROUP FLAT
    static const char grpdef[] = {2,0xFF,2,0xFF,3,0xFF,4};

    objsegdef(obj.csegattr,codesize,3,4);	// seg _TEXT, class CODE

#if MARS
    dsegattr = I32
	  ? SEG_ATTR(SEG_ALIGN16,SEG_C_PUBLIC,0,USE32)
	  : SEG_ATTR(SEG_ALIGN16,SEG_C_PUBLIC,0,USE16);
#else
    dsegattr = I32
	  ? SEG_ATTR(SEG_ALIGN4,SEG_C_PUBLIC,0,USE32)
	  : SEG_ATTR(SEG_ALIGN2,SEG_C_PUBLIC,0,USE16);
#endif

    objsegdef(dsegattr,datasize,5,DATACLASS);	// seg _DATA, class DATA
    objsegdef(dsegattr,cdatasize,7,7);		// seg CONST, class CONST
    objsegdef(dsegattr,udatasize,8,9);		// seg _BSS, class BSS

    obj.lnameidx = 10;				// next lname index
    obj.segidx = 5;				// next segment index

    if (config.fulltypes)
    {
	dsymattr = I32
	      ? SEG_ATTR(SEG_ALIGN1,SEG_C_ABS,0,USE32)
	      : SEG_ATTR(SEG_ALIGN1,SEG_C_ABS,0,USE16);

	if (config.exe & EX_flat)
	{   // IBM's version of CV uses dword aligned segments
	    dsymattr = SEG_ATTR(SEG_ALIGN4,SEG_C_ABS,0,USE32);
	}
	else if (config.fulltypes == CV4)
	{   // Always use 32 bit segments
	    dsymattr |= USE32;
	    assert(!(config.flags & CFGeasyomf));
	}
	objsegdef(dsymattr,SegData[DEBSYM]->SDoffset,0x0C,0x0D);
	objsegdef(dsymattr,SegData[DEBTYP]->SDoffset,0x0A,0x0B);
	obj.lnameidx += 4;			// next lname index
	obj.segidx += 2;			// next segment index
    }

    objrecord(GRPDEF,grpdef,(config.exe & EX_flat) ? 1 : sizeof(grpdef));
#if 0
    // Define fixup threads, we don't use them
    {	static const char thread[] = { 0,3,1,2,2,1,3,4,0x40,1,0x45,1 };
	objrecord(obj.mfixupp,thread,sizeof(thread));
    }
    // This comment appears to indicate that no more PUBDEFs, EXTDEFs,
    // or COMDEFs are coming.
    {	static const char cv[] = {0,0xA2,1};
	objrecord(COMENT,cv,sizeof(cv));
    }
#endif
}

//#if NEWSTATICDTOR

/**************************************
 * Symbol is the function that calls the static constructors.
 * Put a pointer to it into a special segment that the startup code
 * looks at.
 * Input:
 *	s	static constructor function
 *	dtor	number of static destructors
 *	seg	1:	user
 *		2:	lib
 *		3:	compiler
 */

void obj_staticctor(Symbol *s,int dtor,int seg)
{
    // We need to always put out the segments in triples, so that the
    // linker will put them in the correct order.
    static char lnamector[] = "\05XIFCB\04XIFU\04XIFL\04XIFM\05XIFCE";
    static char lnamedtor[] = "\04XOFB\03XOF\04XOFE";
    static char lnamedtorf[] = "\03XOB\02XO\03XOE";

    int dsegattr;
    int i;

    symbol_debug(s);

    // Determine if near or far function
    assert(I32 || tyfarfunc(s->ty()));

    // Put out LNAMES record
    objrecord(LNAMES,lnamector,sizeof(lnamector) - 1);

    dsegattr = I32
	? SEG_ATTR(SEG_ALIGN4,SEG_C_PUBLIC,0,USE32)
	: SEG_ATTR(SEG_ALIGN2,SEG_C_PUBLIC,0,USE16);

    for (i = 0; i < 5; i++)
    {	int sz;

	sz = (i == seg) ? 4 : 0;

	// Put out segment definition record
	objsegdef(dsegattr,sz,obj.lnameidx,DATACLASS);

	if (i == seg)
	    reftoident(obj.segidx,0,s,0,0);	// put out function pointer

	obj.lnameidx++;

	seg_data *pseg = getsegment();
	pseg->SDseg = obj.segidx++;
    }

    if (dtor)
    {	// Leave space in XOF segment so that __fatexit() can insert a
	// pointer to the static destructor in XOF.

	// Put out LNAMES record
	if (LARGEDATA)
	    objrecord(LNAMES,lnamedtorf,sizeof(lnamedtorf) - 1);
	else
	    objrecord(LNAMES,lnamedtor,sizeof(lnamedtor) - 1);

	// Put out beginning segment
	objsegdef(dsegattr,0,obj.lnameidx,BSSCLASS);

	// Put out segment definition record
	objsegdef(dsegattr,4 * dtor,obj.lnameidx + 1,BSSCLASS);

	// Put out ending segment
	objsegdef(dsegattr,0,obj.lnameidx + 2,BSSCLASS);

	obj.lnameidx += 3;			// for next time
	obj.segidx += 3;
    }
}

//#else

/***************************************
 * Stuff pointer to function in its own segment.
 * Used for static ctor and dtor lists.
 */

void obj_funcptr(Symbol *s)
{
    // We need to always put out the segments in triples, so that the
    // linker will put them in the correct order.
    static char *lnames[5+4+5] =
    {	
		"\03XIB\02XI\03XIE",		// near constructor
		"\03XCB\02XC\03xCE",		// near destructor
		"\04XIFB\03XIF\04XIFE",		// far constructor
		"\04XCFB\03XCF\04XCFE"		// far destructor
    };
    // Size of each of the above strings
    static int lnamesize[4] = { 4+3+4,4+3+4,5+4+5,5+4+5 };

    int dsegattr;
    int i;

    symbol_debug(s);
#ifdef DEBUG
    assert(memcmp(s->Sident,"_ST",3) == 0);
#endif

    // Determine if constructor or destructor
    // _STI... is a constructor, _STD... is a destructor
    i = s->Sident[3] == 'D';
    // Determine if near or far function
    if (tyfarfunc(s->Stype->Tty))
	i += 2;

    // Put out LNAMES record
    objrecord(LNAMES,lnames[i],lnamesize[i]);

    dsegattr = I32
	? SEG_ATTR(SEG_ALIGN4,SEG_C_PUBLIC,0,USE32)
	: SEG_ATTR(SEG_ALIGN2,SEG_C_PUBLIC,0,USE16);

    // Put out beginning segment
    objsegdef(dsegattr,0,obj.lnameidx,DATACLASS);
    obj.segidx++;

    // Put out segment definition record
    // size is NPTRSIZE or FPTRSIZE
    objsegdef(dsegattr,(i & 2) + tysize[TYnptr],obj.lnameidx + 1,DATACLASS);
    seg_data *pseg = getsegment();
    pseg->SDseg = obj.segidx;
    reftoident(obj.segidx,0,s,0,0);	// put out function pointer
    obj.segidx++;

    // Put out ending segment
    objsegdef(dsegattr,0,obj.lnameidx + 2,DATACLASS);
    obj.segidx++;

    obj.lnameidx += 3;			// for next time
}

//#endif

/***************************************
 * Stuff pointer to function in its own segment.
 * Used for static ctor and dtor lists.
 */

void obj_ehtables(Symbol *sfunc,targ_size_t size,Symbol *ehsym)
{
    // We need to always put out the segments in triples, so that the
    // linker will put them in the correct order.
    static char lnames[] =
    {	"\03FIB\02FI\03FIE"		// near constructor
    };
    int i;
    int dsegattr;
    targ_size_t offset;

    symbol_debug(sfunc);

    if (obj.fiseg == 0)
    {
	// Put out LNAMES record
	objrecord(LNAMES,lnames,sizeof(lnames) - 1);

	dsegattr = I32
	    ? SEG_ATTR(SEG_ALIGN4,SEG_C_PUBLIC,0,USE32)
	    : SEG_ATTR(SEG_ALIGN2,SEG_C_PUBLIC,0,USE16);

	// Put out beginning segment
	objsegdef(dsegattr,0,obj.lnameidx,DATACLASS);
	obj.lnameidx++;
	obj.segidx++;

	// Put out segment definition record
	obj.fisegi = obj_newfarseg(0,DATACLASS);
	objsegdef(dsegattr,0,obj.lnameidx,DATACLASS);
	SegData[obj.fisegi]->attr = dsegattr;
	obj.fiseg = obj.segidx;

	// Put out ending segment
	objsegdef(dsegattr,0,obj.lnameidx + 1,DATACLASS);

	obj.lnameidx += 2;		// for next time
	obj.segidx += 2;
    }
    offset = SegData[obj.fisegi]->SDoffset;
    offset += reftoident(obj.fiseg,offset,sfunc,0,LARGECODE ? CFoff | CFseg : CFoff);	// put out function pointer
    offset += reftoident(obj.fiseg,offset,ehsym,0,0);	// pointer to data
    obj_bytes(obj.fiseg,offset,intsize,&size);		// size of function
    SegData[obj.fisegi]->SDoffset = offset + intsize;
}

/***************************************
 * Append pointer to ModuleInfo to "FM" segment.
 * The FM segment is bracketed by the empty FMB and FME segments.
 */

#if MARS

void obj_moduleinfo(Symbol *scc)
{
    // We need to always put out the segments in triples, so that the
    // linker will put them in the correct order.
    static char lnames[] =
    {	"\03FMB\02FM\03FME"
    };
    int i;
    int dsegattr;
    targ_size_t offset;

    symbol_debug(scc);

    if (obj.fmseg == 0)
    {
	// Put out LNAMES record
	objrecord(LNAMES,lnames,sizeof(lnames) - 1);

	dsegattr = I32
	    ? SEG_ATTR(SEG_ALIGN4,SEG_C_PUBLIC,0,USE32)
	    : SEG_ATTR(SEG_ALIGN2,SEG_C_PUBLIC,0,USE16);

	// Put out beginning segment
	objsegdef(dsegattr,0,obj.lnameidx,DATACLASS);
	obj.lnameidx++;
	obj.segidx++;

	// Put out segment definition record
	obj.fmsegi = obj_newfarseg(0,DATACLASS);
	objsegdef(dsegattr,0,obj.lnameidx,DATACLASS);
	SegData[obj.fmsegi]->attr = dsegattr;
	obj.fmseg = obj.segidx;

	// Put out ending segment
	objsegdef(dsegattr,0,obj.lnameidx + 1,DATACLASS);

	obj.lnameidx += 2;		// for next time
	obj.segidx += 2;
    }
    offset = SegData[obj.fmsegi]->SDoffset;
    offset += reftoident(obj.fmseg,offset,scc,0,LARGECODE ? CFoff | CFseg : CFoff);	// put out function pointer
    SegData[obj.fmsegi]->SDoffset = offset;
}

#endif


/*********************************
 * Setup for Symbol s to go into a COMDAT segment.
 * Output (if s is a function):
 *	cseg		segment index of new current code segment
 *	Coffset		starting offset in cseg
 * Returns:
 *	"segment index" of COMDAT (which will be a negative value to
 *	distinguish it from regular segments).
 */

int obj_comdat(Symbol *s)
{   char lnames[IDMAX+IDOHD+1];	// +1 to allow room for strcpy() terminating 0
    char cextdef[2+2];
    char __ss *p;
    size_t lnamesize;
    unsigned ti;
    int isfunc;
    tym_t ty;

    symbol_debug(s);
    ty = s->ty();
    isfunc = tyfunc(ty) != 0;

    // Put out LNAME for name of Symbol
    lnamesize = obj_mangle(s,lnames);
    objrecord((s->Sclass == SCstatic ? LLNAMES : LNAMES),lnames,lnamesize);

    // Put out CEXTDEF for name of Symbol
    outextdata();
    p = cextdef;
    p += insidx(p,obj.lnameidx++);
    ti = (config.fulltypes == CVOLD) ? cv_typidx(s->Stype) : 0;
    p += instypidx(p,ti);
    objrecord(CEXTDEF,cextdef,p - cextdef);
    s->Sxtrnnum = ++obj.extidx;

    // Start new LEDATA record for this COMDAT
    ledata_new(-obj.extidx,0);
    obj.ledata->typidx = ti;
    obj.ledata->pubnamidx = obj.lnameidx - 1;
    if (isfunc)
    {	obj.ledata->pubbase = cseg;
	if (s->Sclass == SCcomdat || s->Sclass == SCinline
#if MARS
	    //|| s->Sclass == SCglobal
#endif
	   )
	    obj.ledata->alloctyp = 0x10 | 0x00;	// pick any instance | explicit allocation
	cseg = obj.ledata->lseg;
	obj.pubnamidx = obj.lnameidx - 1;
	Coffset = 0;
	if (tyfarfunc(ty) && strcmp(s->Sident,"main") == 0)
	    obj.ledata->alloctyp |= 1;	// because MS does for unknown reasons
    }
    else
    {	unsigned char atyp;

	switch (ty & mTYLINK)
	{   case 0:
	    case mTYnear:	obj.ledata->pubbase = DATA;
#if 0
				atyp = 0;	// only one instance is allowed
#else
				atyp = 0x10;	// pick any (also means it is
						// not searched for in a library)
#endif
				break;

	    case mTYcs:		obj.ledata->flags |= 0x08;	// data in code seg
				atyp = 0x11;	break;

	    case mTYfar:	atyp = 0x12;	break;

	    case mTYthread:	obj.ledata->pubbase = obj_tlsseg()->SDseg;
				atyp = 0x10;	// pick any (also means it is
						// not searched for in a library)
				break;

	    default:		assert(0);
	}
	obj.ledata->alloctyp = atyp;
    }
    if (s->Sclass == SCstatic)
	obj.ledata->flags |= 0x04;	// local bit (make it an "LCOMDAT")
    seg_data *pseg = getsegment();
    pseg->SDseg = -obj.extidx;
    return -obj.extidx;
}

/**********************************
 * Reset code seg to existing seg.
 * Used after a COMDAT for a function is done.
 */

void obj_setcodeseg(int seg,targ_size_t offset)
{
    assert(0 < seg && seg < obj.segidx);
    cseg = seg;
    Coffset = offset;
}

/********************************
 * Define a new code segment.
 * Input:
 *	name		name of segment, if NULL then revert to default
 *	suffix	0	use name as is
 *		1	append "_TEXT" to name
 * Output:
 *	cseg		segment index of new current code segment
 *	Coffset		starting offset in cseg
 * Returns:
 *	segment index of newly created code segment
 */

int obj_codeseg(char *name,int suffix)
{
    char *lnames;
    size_t lnamesize;

    if (!name)
    {
	if (cseg != CODE)
	{
	    if (obj.lastfarcodesegi != -1)
		SegData[obj.lastfarcodesegi]->SDoffset = Coffset;
	    obj.lastfarcodesegi = -1;
	    Coffset = obj.codeSegOffset;
	    cseg = CODE;
	}
	return cseg;
    }

    // Put out LNAMES record
    lnamesize = strlen(name) + suffix * 5;
    lnames = (char *) alloca(1 + lnamesize + 1);
    lnames[0] = lnamesize;
    assert(lnamesize <= (255 - 2 - sizeof(int)*3));
    strcpy(lnames + 1,name);
    if (suffix)
	strcat(lnames + 1,"_TEXT");
    objrecord(LNAMES,lnames,lnamesize + 1);

    if (obj.lastfarcodesegi == -1)
	obj.codeSegOffset = Coffset;		// size of default code seg
    else
	SegData[obj.lastfarcodesegi]->SDoffset = Coffset;
    obj.lastfarcodesegi = obj_newfarseg(0,4);
    SegData[obj.lastfarcodesegi]->attr = obj.csegattr;
    cseg = obj.segidx;			// new code segment index
    Coffset = 0;

    objsegdef(obj.csegattr,0,obj.lnameidx++,4);

    return obj.segidx++;
}

/*********************************
 * Define segment for Thread Local Storage.
 * Output:
 *	tlsseg	set to segment number for TLS segment.
 * Returns:
 *	segment for TLS segment
 */

seg_data *obj_tlsseg_bss() { return obj_tlsseg(); }

seg_data *obj_tlsseg()
{   //static char tlssegname[] = "\04$TLS\04$TLS";
    //static char tlssegname[] = "\05.tls$\03tls";
    static const char tlssegname[] = "\05.tls$\03tls\04.tls\010.tls$ZZZ";

    if (obj.tlsseg == UNKNOWN)
    {   int segattr;

	objrecord(LNAMES,tlssegname,sizeof(tlssegname) - 1);

	segattr = I32
	    ? SEG_ATTR(SEG_ALIGN4,SEG_C_PUBLIC,0,USE32)
	    : SEG_ATTR(SEG_ALIGN2,SEG_C_PUBLIC,0,USE16);

	// Put out beginning segment (.tls)
	objsegdef(segattr,0,obj.lnameidx + 2,obj.lnameidx + 1);
	obj.segidx++;

	// Put out .tls$ segment definition record
	obj.tlssegi = obj_newfarseg(0,obj.lnameidx + 1);
	obj.tlsseg = obj.segidx;
	objsegdef(segattr,0,obj.lnameidx,obj.lnameidx + 1);
	SegData[obj.tlssegi]->attr = segattr;
	SegData[obj.tlssegi]->SDseg = obj.tlsseg;

	// Put out ending segment (.tls$ZZZ)
	objsegdef(segattr,0,obj.lnameidx + 3,obj.lnameidx + 1);

	obj.lnameidx += 4;
	obj.segidx += 2;
    }
    return SegData[obj.tlssegi];
}


/********************************
 * Define a far data segment.
 * Input:
 *	name	Name of module
 *	size	Size of the segment to be created
 * Returns:
 *	segment index of far data segment created
 */

int obj_fardata(char *name,targ_size_t size,targ_size_t *poffset)
{
    static char fardataclass[] = "\010FAR_DATA";
    int len;
    int i;
    char *buffer;

    // See if we can use existing far segment, and just bump its size
    i = obj.lastfardatasegi;
    if (i != -1
	&& (intsize != 2 || (unsigned long) SegData[i]->SDoffset + size < 0x8000)
	)
    {	*poffset = SegData[i]->SDoffset;	// BUG: should align this
	SegData[i]->SDoffset += size;
	return SegData[i]->seg;
    }

    // No. We need to build a new far segment

    if (obj.fardataidx == 0)		// if haven't put out far data lname
    {	// Put out class lname
	objrecord(LNAMES,fardataclass,sizeof(fardataclass) - 1);
	obj.fardataidx = obj.lnameidx++;
    }

    // Generate name based on module name
    name = strupr(filespecgetroot(filespecname(obj.modname)));

    // Generate name for this far segment
    len = 1 + strlen(name) + 3 + 5 + 1;
    buffer = (char *)alloca(len);
    sprintf(buffer + 1,"%s%d_DATA",name,obj.segidx);
    len = strlen(buffer + 1);
    buffer[0] = len;
    assert(len <= 255);
    objrecord(LNAMES,buffer,len + 1);

    mem_free(name);

    // Construct a new SegData[] entry
    obj.lastfardatasegi = obj_newfarseg(size,obj.fardataidx);

    // Generate segment definition
    objsegdef(obj.fdsegattr,size,obj.lnameidx++,obj.fardataidx);

    *poffset = 0;
    return obj.segidx++;
}

/************************************
 * Remember where we put a far segment so we can adjust
 * its size later.
 * Input:
 *	obj.segidx
 *	lnameidx
 * Returns:
 *	index of SegData[]
 */

STATIC int obj_newfarseg(targ_size_t size,int classidx)
{
#if 1
    seg_data *f;
    f = getsegment();
    int i = seg_count;
    f->SDseg = obj.segidx;
#else
    int i = obj.farsegi++;
    Farseg *f;

    obj.farseg = (Farseg *) mem_realloc(obj.farseg,sizeof(Farseg) * obj.farsegi);
    f = &SegData[i];
#endif
    f->isfarseg = true;
    f->seek = obj.buf->size();
    f->seg = obj.segidx;
    f->attr = obj.fdsegattr;
    f->origsize = size;
    f->SDoffset = size;
    f->lnameidx = obj.lnameidx;
    f->classidx = classidx;
    return i;
}

/******************************
 * Convert reference to imported name.
 */

void obj_import(elem *e)
{
#if MARS
    assert(0);
#else
    Symbol *s;
    Symbol *simp;

    elem_debug(e);
    if ((e->Eoper == OPvar || e->Eoper == OPrelconst) &&
	(s = e->EV.sp.Vsym)->ty() & mTYimport &&
	(s->Sclass == SCextern || s->Sclass == SCinline)
       )
    {	char *name;
	char *p;
	size_t len;
	char buffer[IDMAX + IDOHD + 1];

	// Create import name
	len = obj_mangle(s,buffer);
	if (buffer[0] == (char)0xFF && buffer[1] == 0)
	{   name = buffer + 4;
	    len -= 4;
	}
	else
	{   name = buffer + 1;
	    len -= 1;
	}
	p = (char *) alloca(5 + len + 1);
	memcpy(p,"_imp_",5);
	memcpy(p + 5,name,len);
	p[5 + len] = 0;

	simp = scope_search(p,SCTglobal);
	if (!simp)
	{   type *t;

	    simp = scope_define(p,SCTglobal,SCextern);
	    simp->Ssequence = 0;
	    simp->Sfl = FLextern;
	    simp->Simport = s;
	    t = newpointer(s->Stype);
	    t->Tmangle = mTYman_c;
	    t->Tcount++;
	    simp->Stype = t;
	}
	assert(!e->EV.sp.Voffset);
	if (e->Eoper == OPrelconst)
	{
	    e->Eoper = OPvar;
	    e->EV.sp.Vsym = simp;
	}
	else // OPvar
	{
	    e->Eoper = OPind;
	    e->E1 = el_var(simp);
	    e->E2 = NULL;
	}
    }
#endif
}

/*******************************
 * Mangle a name.
 * Returns:
 *	length of mangled name
 */

size_t obj_mangle(Symbol *s,char *dest)
{   size_t len;
    size_t ilen;
    char *name;
    char *name2 = NULL;

    //printf("obj_mangle('%s'), mangle = x%x\n",s->Sident,type_mangle(s->Stype));
#if SCPP
    name = CPP ? cpp_mangle(s) : s->Sident;
#elif MARS
    name = cpp_mangle(s);
#else
    name = s->Sident;
#endif
    len = strlen(name);			// # of bytes in name

    // Use as max length the max length lib.exe can handle
    // Use 5 as length of _ + @nnn
    #define LIBIDMAX ((512 - 0x25 - 3 - 4) - 5)
#define LIBIDMAX 128
    if (len > LIBIDMAX)
    //if (len > IDMAX)
    {
	size_t len2;

	// Attempt to compress the name
	name2 = id_compress(name, len);
	len2  = strlen(name2);
#if MARS
	if (len2 > LIBIDMAX)		// still too long
	{
	    /* Form md5 digest of the name and store it in the
	     * last 32 bytes of the name.
	     */
	    MD5_CTX mdContext;
	    MD5Init(&mdContext);
	    MD5Update(&mdContext, (unsigned char *)name, len);
	    MD5Final(&mdContext);
	    memcpy(name2, name, LIBIDMAX - 32);
	    for (int i = 0; i < 16; i++)
	    {	unsigned char c = mdContext.digest[i];
		unsigned char c1 = (c >> 4) & 0x0F;
		unsigned char c2 = c & 0x0F;
		c1 += (c1 < 10) ? '0' : 'A' - 10;
		name2[LIBIDMAX - 32 + i * 2] = c1;
		c2 += (c2 < 10) ? '0' : 'A' - 10;
		name2[LIBIDMAX - 32 + i * 2 + 1] = c2;
	    }
	    name = name2;
	    len = LIBIDMAX;
	    name[len] = 0;
	    //printf("name = '%s', len = %d, strlen = %d\n", name, len, strlen(name));
	}
#else
	if (len2 > IDMAX)		// still too long
	{
#if SCPP
	    synerr(EM_identifier_too_long, name, len - IDMAX, IDMAX);
#elif MARS
//	    error(0, "identifier %s is too long by %d characters", name, len - IDMAX);
#else
	    assert(0);
#endif
	    len = IDMAX;
	}
#endif
	else
	{
	    name = name2;
	    len = len2;
	}
    }
    ilen = len;
    if (ilen > (255-2-sizeof(int)*3))
	dest += 3;
    switch (type_mangle(s->Stype))
    {	case mTYman_pas:		// if upper case
	case mTYman_for:
	    memcpy(dest + 1,name,len);	// copy in name
	    dest[1 + len] = 0;
	    strupr(dest + 1);		// to upper case
	    break;
#if SCPP || MARS
	case mTYman_cpp:
#if NEWMANGLE
	    memcpy(dest + 1,name,len);
	    break;
#endif
#endif
	case mTYman_std:
	    if (!(config.flags4 & CFG4oldstdmangle) &&
		config.exe == EX_NT && tyfunc(s->ty()) &&
		!variadic(s->Stype))
	    {
		dest[1] = '_';
		memcpy(dest + 2,name,len);
		dest[1 + 1 + len] = '@';
		itoa(type_paramsize(s->Stype),dest + 3 + len,10);
		len = strlen(dest + 1);
		assert(isdigit(dest[len]));
		break;
	    }
	case mTYman_c:
	    dest[1] = '_';		// leading _ in name
	    memcpy(&dest[2],name,len);	// copy in name
	    len++;
	    break;

	case mTYman_d:
	case mTYman_sys:
	    memcpy(dest + 1, name, len);	// no mangling
	    dest[1 + len] = 0;
	    break;
	default:
#ifdef DEBUG
	    symbol_print(s);
#endif
	    assert(0);
    }
    if (ilen > (255-2-sizeof(int)*3))
    {	dest -= 3;
	dest[0] = 0xFF;
	dest[1] = 0;
#ifdef DEBUG
	assert(len <= 0xFFFF);
#endif
	TOWORD(dest + 2,len);
	len += 4;
    }
    else
    {	*dest = len;
	len++;
    }
    if (name2)
	free(name2);
    assert(len <= IDMAX + IDOHD);
    return len;
}

/*******************************
 * Export a function name.
 */

void obj_export(Symbol *s,unsigned argsize)
{   char *coment;
    size_t len;

    coment = (char *) alloca(4 + 1 + (IDMAX + IDOHD) + 1); // allow extra byte for mangling
    len = obj_mangle(s,&coment[4]);
    assert(len <= IDMAX + IDOHD);
    coment[1] = 0xA0;				// comment class
    coment[2] = 2;				// why??? who knows
    if (argsize >= 64)				// we only have a 5 bit field
	argsize = 0;				// hope we don't need callgate
    coment[3] = (argsize + 1) >> 1;		// # words on stack
    coment[4 + len] = 0;			// no internal name
    objrecord(COMENT,coment,4 + len + 1);	// module name record
}

/********************************
 * Output a public definition.
 * Input:
 *	seg =		segment index that symbol is defined in
 *	s ->		symbol
 *	offset =	offset of name
 */

STATIC void outpubdata()
{
    if (obj.pubdatai)
    {	objrecord(obj.mpubdef,obj.pubdata,obj.pubdatai);
	obj.pubdatai = 0;
    }
}

void objpubdef(int seg,Symbol *s,targ_size_t offset)
{   unsigned reclen,len;
    char *p;
    unsigned ti;

    _chkstack();

    if (obj.pubdatai + 1 + (IDMAX + IDOHD) + 4 + 2 > sizeof(obj.pubdata) ||
	seg != getindex(obj.pubdata + 1))
	outpubdata();
    if (obj.pubdatai == 0)
    {
	obj.pubdata[0] = (seg == DATA || seg == UDATA) ? 1 : 0; // group index
	obj.pubdatai += 1 + insidx(obj.pubdata + 1,seg);	// segment index
    }
    p = &obj.pubdata[obj.pubdatai];
    len = obj_mangle(s,p);		// mangle in name
    reclen = len + intsize;
    p += len;
    TOOFFSET(p,offset);
    p += intsize;
    ti = (config.fulltypes == CVOLD) ? cv_typidx(s->Stype) : 0;
    reclen += instypidx(p,ti);
    obj.pubdatai += reclen;
}

/*******************************
 * Output an external definition.
 * Input:
 *	name ->	external identifier
 * Returns:
 *	External index of the definition (1,2,...)
 */

STATIC void outextdata()
{
    if (obj.extdatai)
    {	objrecord(EXTDEF,obj.extdata,obj.extdatai);
	obj.extdatai = 0;
    }
}

int objextdef(const char *name)
{   unsigned len;
    char *e;

    _chkstack();
    //dbg_printf("objextdef('%s')\n",name);
    assert(name);
    len = strlen(name);			// length of identifier
    if (obj.extdatai + len + ONS_OHD + 1 > sizeof(obj.extdata))
	outextdata();

    e = &obj.extdata[obj.extdatai];
    len = obj_namestring(e,name);
    e[len] = 0;				// typidx = 0
    obj.extdatai += len + 1;
    assert(obj.extdatai <= sizeof(obj.extdata));
    return ++obj.extidx;
}

/*******************************
 * Output an external definition.
 * Input:
 *	s	Symbol to do EXTDEF on
 * Returns:
 *	External index of the definition (1,2,...)
 */

int objextern(Symbol *s)
{   unsigned len;
    char *e;

    _chkstack();
    //dbg_printf("objextern('%s')\n",s->Sident);
    symbol_debug(s);
    if (obj.extdatai + (IDMAX + IDOHD) + 3 > sizeof(obj.extdata))
	outextdata();

    e = &obj.extdata[obj.extdatai];
    len = obj_mangle(s,e);
    e[len] = 0;			// typidx = 0
    obj.extdatai += len + 1;
    s->Sxtrnnum = ++obj.extidx;
    return obj.extidx;
}

/*******************************
 * Output a common block definition.
 * Input:
 *	p ->	external identifier
 *	flag	TRUE:	in default data segment
 *		FALSE:	not in default data segment
 *	size	size in bytes of each elem
 *	count	number of elems
 * Returns:
 *	External index of the definition (1,2,...)
 */

// Helper for obj_comdef()

static unsigned storelength(unsigned long length,unsigned i)
{
    obj.extdata[i] = length;
    if (length >= 128)	// Microsoft docs say 129, but their linker
			// won't take >=128, so accommodate it
    {	obj.extdata[i] = 129;
#ifdef DEBUG
	assert(length <= 0xFFFF);
#endif
	TOWORD(obj.extdata + i + 1,length);
	if (length >= 0x10000)
	{   obj.extdata[i] = 132;
	    obj.extdata[i + 3] = length >> 16;

	    // Only 386 can generate lengths this big
	    if (I32 && length >= 0x1000000)
	    {	obj.extdata[i] = 136;
		obj.extdata[i + 4] = length >> 24;
		i += 4;
	    }
	    else
		i += 3;
	}
	else
	    i += 2;
    }
    return i + 1;		// index past where we stuffed length
}

int obj_comdef(Symbol *s,int flag,targ_size_t size,targ_size_t count)
{ register unsigned i;
  unsigned long length;
  unsigned ti;

    //dbg_printf("obj_comdef('%s',%d,%d,%d)\n",s->Sident,flag,size,count);
    outextdata();		// borrow the extdata[] storage
    i = obj_mangle(s,obj.extdata);

    ti = (config.fulltypes == CVOLD) ? cv_typidx(s->Stype) : 0;
    i += instypidx(obj.extdata + i,ti);

  if (flag)				// if in default data segment
  {
	//printf("NEAR comdef\n");
	obj.extdata[i] = 0x62;
	length = (unsigned long) size * count;
	assert(I32 || length <= 0x10000);
	i = storelength(length,i + 1);
  }
  else
  {
	//printf("FAR comdef\n");
	obj.extdata[i] = 0x61;
	i = storelength((unsigned long) size,i + 1);
	i = storelength((unsigned long) count,i);
  }
  assert(i <= arraysize(obj.extdata));
  objrecord(COMDEF,obj.extdata,i);
  return ++obj.extidx;
}

/***************************************
 * Append an iterated data block of 0s.
 * (uninitialized data only)
 */

void obj_write_zeros(seg_data *pseg, targ_size_t count)
{
    obj_lidata(pseg->SDseg, pseg->SDoffset, count);
    pseg->SDoffset += count;
}

/***************************************
 * Output an iterated data block of 0s.
 * (uninitialized data only)
 */

void obj_lidata(int seg,targ_size_t offset,targ_size_t count)
{   int i;
    unsigned reclen;
    static char zero[20];
    char data[20];
    char __ss *di;

    //printf("obj_lidata(seg = %d, offset = x%x, count = %d)\n", seg, offset, count);
    if (count <= sizeof(zero))		// if shorter to use ledata
    {
	obj_bytes(seg,offset,count,zero);
	return;
    }

    if (seg < 0)
    {
	while (count > sizeof(zero))
	{
	    obj_bytes(seg,offset,sizeof(zero),zero);
	    count -= sizeof(zero);
	}
	obj_bytes(seg,offset,count,zero);
	return;
    }

    i = insidx(data,seg);
    di = data + i;
    TOOFFSET(di,offset);

    if (config.flags & CFGeasyomf)
    {
	if (count >= 0x8000)		// repeat count can only go to 32k
	{
	    TOWORD(di + 4,(unsigned short)(count / 0x8000));
	    TOWORD(di + 4 + 2,1);		// 1 data block follows
	    TOWORD(di + 4 + 2 + 2,0x8000);	// repeat count
	    TOWORD(di + 4 + 2 + 2 + 2,0);	// block count
	    TOWORD(di + 4 + 2 + 2 + 2 + 2,1);	// 1 byte of 0
	    reclen = i + 4 + 5 * 2;
	    objrecord(obj.mlidata,data,reclen);
	    obj_lidata(seg,offset + (count & ~0x7FFFL),count & 0x7FFF);
	}
	else
	{
	    TOWORD(di + 4,(unsigned short)count);	// repeat count
	    TOWORD(di + 4 + 2,0);			// block count
	    TOWORD(di + 4 + 2 + 2,1);			// 1 byte of 0
	    reclen = i + 4 + 2 + 2 + 2;
	    objrecord(obj.mlidata,data,reclen);
	}
    }
    else
    {
	TOOFFSET(di + intsize,count);
	TOWORD(di + intsize * 2,0);	// block count
	TOWORD(di + intsize * 2 + 2,1);	// repeat 1 byte of 0s
	reclen = i + (I32 ? 12 : 8);
	objrecord(obj.mlidata,data,reclen);
    }
    assert(reclen <= sizeof(data));
}

/****************************
 * Output a MODEND record.
 */

STATIC void obj_modend()
{
    if (obj.startaddress)
    {   char mdata[10];
	int i;
	unsigned framedatum,targetdatum;
	unsigned char fd;
	targ_size_t offset;
	int external;		// !=0 if identifier is defined externally
	tym_t ty;
	Symbol *s = obj.startaddress;

	// Turn startaddress into a fixup.
	// Borrow heavilly from reftoident()

	symbol_debug(s);
	offset = 0;
	ty = s->ty();

	switch (s->Sclass)
	{
	    case SCcomdat:
	    case_SCcomdat:
	    case SCextern:
	    case SCcomdef:
		if (s->Sxtrnnum)		// identifier is defined somewhere else
		    external = s->Sxtrnnum;
		else
		{
		 Ladd:
		    s->Sclass = SCextern;
		    external = objextern(s);
		    outextdata();
		}
		break;
	    case SCinline:
		if (config.flags2 & CFG2comdat)
		    goto case_SCcomdat;	// treat as initialized common block
	    case SCsinline:
	    case SCstatic:
	    case SCglobal:
		if (s->Sseg == UNKNOWN)
		    goto Ladd;
		if (seg_is_comdat(s->Sseg))		// if in comdat
		    goto case_SCcomdat;
	    case SClocstat:
		external = 0;		// identifier is static or global
					    // and we know its offset
		offset += s->Soffset;
		break;
	    default:
    #ifdef DEBUG
		//symbol_print(s);
    #endif
		assert(0);
	}

	if (external)
	{   fd = FD_T2;
	    targetdatum = external;
	    switch (s->Sfl)
	    {
		case FLextern:
		    if (!(ty & (mTYcs | mTYthread)))
			goto L1;
		case FLfunc:
		case FLfardata:
		case FLcsdata:
		case FLtlsdata:
		    if (config.exe & EX_flat)
		    {   fd |= FD_F1;
			framedatum = 1;
		    }
		    else
		    {
		//case FLtlsdata:
			fd |= FD_F2;
			framedatum = targetdatum;
		    }
		    break;
		default:
		    goto L1;
	    }
	}
	else
	{
	    fd = FD_T0;			// target is always a segment
	    targetdatum = s->Sseg;
	    assert(targetdatum != -1);
	    switch (s->Sfl)
	    {
		case FLextern:
		    if (!(ty & (mTYcs | mTYthread)))
			goto L1;
		case FLfunc:
		case FLfardata:
		case FLcsdata:
		case FLtlsdata:
		    if (config.exe & EX_flat)
		    {   fd |= FD_F1;
			framedatum = 1;
		    }
		    else
		    {
		//case FLtlsdata:
			fd |= FD_F0;
			framedatum = targetdatum;
		    }
		    break;
		default:
		L1:
		    fd |= FD_F1;
		    framedatum = DGROUPIDX;
		    //if (flags == CFseg)
		    {   fd = FD_F1 | FD_T1;	// target is DGROUP
			targetdatum = DGROUPIDX;
		    }
		    break;
	    }
	}

	// Write the fixup into mdata[]
	mdata[0] = 0xC1;
	mdata[1] = fd;
	i = 2 + insidx(&mdata[2],framedatum);
	i += insidx(&mdata[i],targetdatum);
	TOOFFSET(mdata + i,offset);

	objrecord(obj.mmodend,mdata,i + intsize);	// write mdata[] to .OBJ file
    }
    else
    {   static const char modend[] = {0};

	objrecord(MODEND,modend,sizeof(modend));
    }
}

/****************************
 * Output the fixups in list fl.
 */

STATIC void objfixupp(struct FIXUP *f)
{
  unsigned i,j,k;
  targ_size_t locat;
  struct FIXUP *fn;

#if 1	// store in one record
  char data[1024];

  _chkstack();
  i = 0;
  for (; f; f = fn)
  {	unsigned char fd;

	if (i >= sizeof(data) - (3 + 2 + 2))	// if not enough room
	{   objrecord(obj.mfixupp,data,i);
	    i = 0;
	}

	//printf("f = %p, offset = x%x\n",f,f->FUoffset);
	assert(f->FUoffset < 1024);
	locat = (f->FUlcfd & 0xFF00) | f->FUoffset;
	data[i+0] = locat >> 8;
	data[i+1] = locat;
	data[i+2] = fd = f->FUlcfd;
	k = i;
	i += 3 + insidx(&data[i+3],f->FUframedatum);
	if ((fd >> 4) == (fd & 3) && f->FUframedatum == f->FUtargetdatum)
	{
	    data[k + 2] = (fd & 15) | FD_F5;
	}
	else
	    i += insidx(&data[i],f->FUtargetdatum);
	fn = f->FUnext;
	mem_ffree(f);
  }
  assert(i <= sizeof(data));
  if (i)
      objrecord(obj.mfixupp,data,i);
#else	// store in multiple records
  for (; fl; fl = list_next(fl))
  {
	char data[7];

	assert(f->FUoffset < 1024);
	locat = (f->FUlcfd & 0xFF00) | f->FUoffset;
	data[0] = locat >> 8;
	data[1] = locat;
	data[2] = f->FUlcfd;
	i = 3 + insidx(&data[3],f->FUframedatum);
	i += insidx(&data[i],f->FUtargetdatum);
	objrecord(obj.mfixupp,data,i);
  }
#endif
}


/***************************
 * Add a new fixup to the fixup list.
 * Write things out if we overflow the list.
 */

STATIC void addfixup(targ_size_t offset,unsigned lcfd,
	unsigned framedatum,unsigned targetdatum)
{   struct FIXUP *f;

    assert(offset < 0x1024);
#ifdef DEBUG
    assert(targetdatum <= 0x7FFF);
    assert(framedatum <= 0x7FFF);
#endif
    f = (struct FIXUP *) mem_fmalloc(sizeof(struct FIXUP));
    //printf("f = %p, offset = x%x\n",f,offset);
    f->FUoffset = offset;
    f->FUlcfd = lcfd;
    f->FUframedatum = framedatum;
    f->FUtargetdatum = targetdatum;
    f->FUnext = obj.ledata->fixuplist;	// link f into list
    obj.ledata->fixuplist = f;
#ifdef DEBUG
    obj.fixup_count++;			// gather statistics
#endif
}


/*********************************
 * Open up a new ledata record.
 * Input:
 *	seg	segment number data is in
 *	offset	starting offset of start of data for this record
 */

STATIC void ledata_new(int seg,targ_size_t offset)
{

    //dbg_printf("ledata_new(seg = %d, offset = x%lx)\n",seg,offset);
    assert(seg <= 0x7FFF);
    obj.ledata = (Ledatarec *) mem_calloc(sizeof(Ledatarec));
    obj.ledata->lseg = seg;
    obj.ledata->offset = offset;
    if (seg < 0 && offset)	// if continuation of an existing COMDAT
    {   list_t dl;

	for (dl = obj.ledata_list; 1; dl = list_next(dl))
	{   Ledatarec *d;

	    assert(dl);
	    d = list_ledata(dl);
	    if (d->lseg == seg)			// found existing COMDAT
	    {	obj.ledata->flags = d->flags;
		obj.ledata->alloctyp = d->alloctyp;
		obj.ledata->align = d->align;
		obj.ledata->typidx = d->typidx;
		obj.ledata->pubbase = d->pubbase;
		obj.ledata->pubnamidx = d->pubnamidx;
		break;
	    }
	}
    }
    list_prepend(&obj.ledata_list,obj.ledata);
//    list_append(&obj.ledata_list,obj.ledata);
}

/***********************************
 * Append byte to segment.
 */

void obj_write_byte(seg_data *pseg, unsigned byte)
{
    obj_byte(pseg->SDseg, pseg->SDoffset, byte);
    pseg->SDoffset++;
}

/************************************
 * Output byte to object file.
 */

void obj_byte(int seg,targ_size_t offset,unsigned byte)
{   unsigned i;

    if (
	(seg != obj.ledata->lseg ||		// and segments don't match
	 obj.ledata->i > LEDATAMAX - 1 ||	// or it'll overflow
	 offset < obj.ledata->offset ||	// underflow
	 offset > obj.ledata->offset + obj.ledata->i
	)
     )
    {   list_t dl;

	// Try to find an existing ledata
	for (dl = obj.ledata_list; dl; dl = list_next(dl))
	{   Ledatarec *d;

	    d = list_ledata(dl);
	    if (seg == d->lseg &&	// segments match
		offset >= d->offset &&
		offset + 1 <= d->offset + LEDATAMAX &&
		offset <= d->offset + d->i
	       )
	    {
		obj.ledata = d;
		goto L1;
	    }
	}
	ledata_new(seg,offset);
L1:	;
    }

  i = offset - obj.ledata->offset;
  if (obj.ledata->i <= i)
	obj.ledata->i = i + 1;
  obj.ledata->data[i] = byte;		// 1st byte of data
}

/***********************************
 * Append bytes to segment.
 */

void obj_write_bytes(seg_data *pseg, unsigned nbytes, void *p)
{
    obj_bytes(pseg->SDseg, pseg->SDoffset, nbytes, p);
    pseg->SDoffset += nbytes;
}

/************************************
 * Output bytes to object file.
 * Returns:
 *	nbytes
 */

unsigned obj_bytes(int seg,targ_size_t offset,unsigned nbytes, void *p)
{   unsigned n = nbytes;

    //dbg_printf("obj_bytes(seg=%d, offset=x%lx, nbytes=x%x, p=%p)\n",seg,offset,nbytes,p);
 L1:
    if (
	(seg != obj.ledata->lseg ||			// and segments don't match
	 obj.ledata->i + nbytes > LEDATAMAX ||	// or it'll overflow
	 offset < obj.ledata->offset ||		// underflow
	 offset > obj.ledata->offset + obj.ledata->i
	)
     )
    {
	while (nbytes)
	{   
		obj_byte(seg,offset,*(char *)p);
	    offset++;
	    //@((char *)p)++;
		p = ((char*)p+1);
	    nbytes--;
	    if (obj.ledata->i + nbytes <= LEDATAMAX)
		goto L1;
	}
    }
    else
    {	unsigned i;

	i = offset - obj.ledata->offset;
	if (obj.ledata->i < i + nbytes)
	    obj.ledata->i = i + nbytes;
	memcpy(obj.ledata->data + i,p,nbytes);
    }
    return n;
}

/************************************
 * Output word of data. (Two words if segment:offset pair.)
 * Input:
 *	seg	CODE, DATA, CDATA, UDATA
 *	offset	offset of start of data
 *	data	word of data
 *	lcfd	LCxxxx | FDxxxx
 *	if (FD_F2 | FD_T6)
 *		idx1 = external Symbol #
 *	else
 *		idx1 = frame datum
 *		idx2 = target datum
 */

void objledata(int seg,targ_size_t offset,targ_size_t data,
	unsigned lcfd,unsigned idx1,unsigned idx2)
{   unsigned i;
    unsigned size;			// number of bytes to output

    if ((lcfd & LOCxx) == obj.LOCpointer)
	size = tysize[TYfptr];
    else if ((lcfd & LOCxx) == LOCbase)
	size = 2;
    else
	size = tysize[TYnptr];

    if (
	(seg != obj.ledata->lseg ||		// and segments don't match
	 obj.ledata->i + size > LEDATAMAX ||	// or it'll overflow
	 offset < obj.ledata->offset ||	// underflow
	 offset > obj.ledata->offset + obj.ledata->i
	)
     )
    {   list_t dl;

	// Try to find an existing ledata
//dbg_printf("seg = %d, offset = x%lx, size = %d\n",seg,offset,size);
	for (dl = obj.ledata_list; dl; dl = list_next(dl))
	{   Ledatarec *d;

	    d = list_ledata(dl);
//dbg_printf("d: seg = %d, offset = x%lx, i = x%x\n",d->lseg,d->offset,d->i);
	    if (seg == d->lseg &&	// segments match
		offset >= d->offset &&
		offset + size <= d->offset + LEDATAMAX &&
		offset <= d->offset + d->i
	       )
	    {
//dbg_printf("match\n");
		obj.ledata = d;
		goto L1;
	    }
	}
	ledata_new(seg,offset);
L1:	;
    }

    i = offset - obj.ledata->offset;
    if (obj.ledata->i < i + size)
	obj.ledata->i = i + size;
    if (size == 2 || !I32)
	TOWORD(obj.ledata->data + i,data);
    else
	TOLONG(obj.ledata->data + i,data);
    if (size == tysize[TYfptr])		// if doing a seg:offset pair
	TOWORD(obj.ledata->data + i + tysize[TYnptr],0);	// segment portion
    addfixup(offset - obj.ledata->offset,lcfd,idx1,idx2);
}

/************************************
 * Output long word of data.
 * Input:
 *	seg	CODE, DATA, CDATA, UDATA
 *	offset	offset of start of data
 *	data	long word of data
 *   Present only if size == 2:
 *	lcfd	LCxxxx | FDxxxx
 *	if (FD_F2 | FD_T6)
 *		idx1 = external Symbol #
 *	else
 *		idx1 = frame datum
 *		idx2 = target datum
 */

void obj_long(int seg,targ_size_t offset,unsigned long data,
	unsigned lcfd,unsigned idx1,unsigned idx2)
{ unsigned i;

  if (
	(seg != obj.ledata->lseg ||		// or segments don't match
	 obj.ledata->i + tysize[TYfptr] > LEDATAMAX || // or it'll overflow
	 offset < obj.ledata->offset ||	// underflow
	 offset > obj.ledata->offset + obj.ledata->i
	)
     )
	ledata_new(seg,offset);
  i = offset - obj.ledata->offset;
  if (obj.ledata->i < i + tysize[TYfptr])
	obj.ledata->i = i + tysize[TYfptr];
  TOLONG(obj.ledata->data + i,data);
  if (I32)				// if 6 byte far pointers
	TOWORD(obj.ledata->data + i + LONGSIZE,0);		// fill out seg
  addfixup(offset - obj.ledata->offset,lcfd,idx1,idx2);
}

/*******************************
 * Refer to address that is in the data segment.
 * Input:
 *	seg =		where the address is going
 *	offset =	offset within seg
 *	val =		displacement from address
 *	targetdatum =	DATA, CDATA or UDATA, depending where the address is
 *	flags =		CFoff, CFseg
 * Example:
 *	int *abc = &def[3];
 *	to allocate storage:
 *		reftodatseg(DATA,offset,3 * sizeof(int *),UDATA);
 */

void reftodatseg(int seg,targ_size_t offset,targ_size_t val,
	unsigned targetdatum,int flags)
{
    assert(flags);

    if (flags == 0 || flags & CFoff)
    {
	// The frame datum is always 1, which is DGROUP
	objledata(seg,offset,val,
	    LOCATsegrel | obj.LOCoffset | FD_F1 | FD_T4,DGROUPIDX,targetdatum);
	offset += intsize;
    }

    if (flags & CFseg)
    {
#if 0
	if (config.wflags & WFdsnedgroup)
	    warerr(WM_ds_ne_dgroup);
#endif
	objledata(seg,offset,0,
	    LOCATsegrel | LOCbase | FD_F1 | FD_T5,DGROUPIDX,DGROUPIDX);
    }
}

/*******************************
 * Refer to address that is in a far segment.
 * Input:
 *	seg =		where the address is going
 *	offset =	offset within seg
 *	val =		displacement from address
 *	farseg =	far segment index
 *	flags =		CFoff, CFseg
 */

void reftofarseg(int seg,targ_size_t offset,targ_size_t val,
	int farseg,int flags)
{
    assert(flags);

    if (flags == 0 || flags & CFoff)
    {
	objledata(seg,offset,val,
	    LOCATsegrel | obj.LOCoffset | FD_F0 | FD_T4,farseg,farseg);
	offset += intsize;
    }

    if (flags & CFseg)
    {
	objledata(seg,offset,0,
	    LOCATsegrel | LOCbase | FD_F0 | FD_T4,farseg,farseg);
    }
}

/*******************************
 * Refer to address that is in the code segment.
 * Only offsets are output, regardless of the memory model.
 * Used to put values in switch address tables.
 * Input:
 *	seg =		where the address is going (CODE or DATA)
 *	offset =	offset within seg
 *	val =		displacement from start of this module
 */

void reftocodseg(int seg,targ_size_t offset,targ_size_t val)
{   unsigned framedatum;
    unsigned lcfd;
    int idx;

    idx = cseg;
    if (seg_is_comdat(idx))		// if comdat
    {	idx = -idx;
	framedatum = idx;
	lcfd = (LOCATsegrel | obj.LOCoffset) | (FD_F2 | FD_T6);
    }
    else if (config.exe & EX_flat)
    {	framedatum = 1;
	lcfd = (LOCATsegrel | obj.LOCoffset) | (FD_F1 | FD_T4);
    }
    else
    {	framedatum = idx;
	lcfd = (LOCATsegrel | obj.LOCoffset) | (FD_F0 | FD_T4);
    }

    objledata(seg,offset,val,lcfd,framedatum,idx);
}

/*******************************
 * Refer to an identifier.
 * Input:
 *	seg =		where the address is going (CODE or DATA)
 *	offset =	offset within seg
 *	s ->		Symbol table entry for identifier
 *	val =		displacement from identifier
 *	flags =		CFselfrel: self-relative
 *			CFseg: get segment
 *			CFoff: get offset
 * Returns:
 *	number of bytes in reference (2 or 4)
 * Example:
 *	extern int def[];
 *	int *abc = &def[3];
 *	to allocate storage:
 *		reftodatseg(DATA,offset,3 * sizeof(int *),UDATA);
 */

int reftoident(int seg,targ_size_t offset,Symbol *s,targ_size_t val,
	int flags)
{
    unsigned targetdatum;	// which datum the symbol is in
    unsigned framedatum;
    int	    lc;
    int     external;		// !=0 if identifier is defined externally
    int numbytes;
    tym_t ty;

#if 0
    printf("reftoident('%s' seg %d, offset x%lx, val x%lx, flags x%x)\n",
    	s->Sident,seg,offset,val,flags);
    printf("Sseg = %d, Sxtrnnum = %d\n",s->Sseg,s->Sxtrnnum);
    //symbol_print(s);
#endif

    ty = s->ty();
    while (1)
    {
	switch (flags & (CFseg | CFoff))
	{   case 0:
		// Select default
		flags |= CFoff;
		if (tyfunc(ty))
		{
		    if (tyfarfunc(ty))
			flags |= CFseg;
		}
		else // DATA
		{
		    if (LARGEDATA)
			flags |= CFseg;
		}
		continue;
	    case CFoff:
		if (I32)
		{
#if 1
		    if (ty & mTYthread)
		    {	lc = LOC32tlsoffset;
		    }
		    else
#endif
			lc = obj.LOCoffset;
		}
		else
		{
		    // The 'loader_resolved' offset is required for VCM
		    // and Windows support. A fixup of this type is
		    // relocated by the linker to point to a 'thunk'.
		    lc = (tyfarfunc(ty)
			  && !(flags & CFselfrel))
			    ? LOCloader_resolved : obj.LOCoffset;
		}
		numbytes = tysize[TYnptr];
		break;
	    case CFseg:
		lc = LOCbase;
		numbytes = 2;
		break;
	    case CFoff | CFseg:
		lc = obj.LOCpointer;
		numbytes = tysize[TYfptr];
		break;
	}
	break;
    }

    switch (s->Sclass)
    {
	case SCcomdat:
	case_SCcomdat:
	case SCextern:
	case SCcomdef:
	    if (s->Sxtrnnum)		// identifier is defined somewhere else
	    {	external = s->Sxtrnnum;
#ifdef DEBUG
		if (external > obj.extidx)
		    symbol_print(s);
#endif
		assert(external <= obj.extidx);
	    }
	    else
	    {	// Don't know yet, worry about it later
	     Ladd:
		addtofixlist(s,offset,seg,val,flags);
		return numbytes;
	    }
	    break;
	case SCinline:
	    if (config.flags2 & CFG2comdat)
		goto case_SCcomdat;	// treat as initialized common block
	case SCsinline:
	case SCstatic:
	case SCglobal:
	    if (s->Sseg == UNKNOWN)
		goto Ladd;
	    if (seg_is_comdat(s->Sseg))
		goto case_SCcomdat;
	case SClocstat:
	    external = 0;		// identifier is static or global
					// and we know its offset
	    if (flags & CFoff)
		val += s->Soffset;
	    break;
	default:
#ifdef DEBUG
	    symbol_print(s);
#endif
	    assert(0);
    }

    lc |= (flags & CFselfrel) ? LOCATselfrel : LOCATsegrel;
    if (external)
    {	lc |= FD_T6;
	targetdatum = external;
	switch (s->Sfl)
	{
	    case FLextern:
		if (!(ty & (mTYcs | mTYthread)))
		    goto L1;
	    case FLfunc:
	    case FLfardata:
	    case FLcsdata:
	    case FLtlsdata:
		if (config.exe & EX_flat)
		{   lc |= FD_F1;
		    framedatum = 1;
		}
		else
		{
	    //case FLtlsdata:
		    lc |= FD_F2;
		    framedatum = targetdatum;
		}
		break;
	    default:
		goto L1;
	}
    }
    else
    {
	lc |= FD_T4;			// target is always a segment
	targetdatum = s->Sseg;
	assert(targetdatum != UNKNOWN);
	switch (s->Sfl)
	{
	    case FLextern:
		if (!(ty & (mTYcs | mTYthread)))
		    goto L1;
	    case FLfunc:
	    case FLfardata:
	    case FLcsdata:
	    case FLtlsdata:
		if (config.exe & EX_flat)
		{   lc |= FD_F1;
		    framedatum = 1;
		}
		else
		{
	    //case FLtlsdata:
		    lc |= FD_F0;
		    framedatum = targetdatum;
		}
		break;
	    default:
	    L1:
		lc |= FD_F1;
		framedatum = DGROUPIDX;
		if (flags == CFseg)
		{   lc = LOCATsegrel | LOCbase | FD_F1 | FD_T5;
		    targetdatum = DGROUPIDX;
		}
#if 0
		if (flags & CFseg && config.wflags & WFdsnedgroup)
		    warerr(WM_ds_ne_dgroup);
#endif
		break;
	}
    }

    objledata(seg,offset,val,lc,framedatum,targetdatum);
    return numbytes;
}

/*****************************************
 * Generate far16 thunk.
 * Input:
 *	s	Symbol to generate a thunk for
 */

void obj_far16thunk(Symbol *s)
{
    static unsigned char cod32_1[] =
    {
	0x55,				//	PUSH	EBP
	0x8B,0xEC,			//	MOV	EBP,ESP
	0x83,0xEC,0x04,			//	SUB	ESP,4
	0x53,				//	PUSH	EBX
	0x57,				//	PUSH	EDI
	0x56,				//	PUSH	ESI
	0x06,				//	PUSH	ES
	0x8C,0xD2,			//	MOV	DX,SS
	0x80,0xE2,0x03,			//	AND	DL,3
	0x80,0xCA,0x07,			//	OR	DL,7
	0x89,0x65,0xFC,			//	MOV	-4[EBP],ESP
	0x8C,0xD0,			//	MOV	AX,SS
	0x66,0x3D, // 0x00,0x00 */	/*	CMP     AX,seg FLAT:_DATA
    };
    static unsigned char cod32_2[] =
    {	0x0F,0x85,0x10,0x00,0x00,0x00,	//	JNE     L1
	0x8B,0xC4,			//	MOV	EAX,ESP
	0x66,0x3D,0x00,0x08,		//	CMP	AX,2048
	0x0F,0x83,0x04,0x00,0x00,0x00,	//	JAE	L1
	0x66,0x33,0xC0,			//	XOR	AX,AX
	0x94,				//	XCHG	ESP,EAX
					// L1:
	0x55,				//	PUSH	EBP
	0x8B,0xC4,			//	MOV	EAX,ESP
	0x16,				//	PUSH	SS
	0x50,				//	PUSH	EAX
	0x8D,0x75,0x08,			//	LEA	ESI,8[EBP]
	0x81,0xEC,0x00,0x00,0x00,0x00,	//	SUB	ESP,numparam
	0x8B,0xFC,			//	MOV	EDI,ESP
	0xB9,0x00,0x00,0x00,0x00,	//	MOV	ECX,numparam
	0x66,0xF3,0xA4,			//	REP	MOVSB
	0x8B,0xC4,			//	MOV	EAX,ESP
	0xC1,0xC8,0x10,			//	ROR	EAX,16
	0x66,0xC1,0xE0,0x03,		//	SHL	AX,3
	0x0A,0xC2,			//	OR	AL,DL
	0xC1,0xC0,0x10,			//	ROL	EAX,16
	0x50,				//	PUSH	EAX
	0x66,0x0F,0xB2,0x24,0x24,	//	LSS	SP,[ESP]
	0x66,0xEA, // 0,0,0,0, */	/*	JMPF	L3
    };
    static unsigned char cod32_3[] =
    {					// L2:
	0xC1,0xE0,0x10,			//	SHL	EAX,16
	0x0F,0xAC,0xD0,0x10,		//	SHRD	EAX,EDX,16
	0x0F,0xB7,0xE4,			//	MOVZX	ESP,SP
	0x0F,0xB2,0x24,0x24,		//	LSS	ESP,[ESP]
	0x5D,				//	POP	EBP
	0x8B,0x65,0xFC,			//	MOV	ESP,-4[EBP]
	0x07,				//	POP	ES
	0x5E,				//	POP	ESI
	0x5F,				//	POP	EDI
	0x5B,				//	POP	EBX
	0xC9,				//	LEAVE
	0xC2,0x00,0x00			//	RET	numparam
    };

    unsigned numparam = 24;
    targ_size_t L2offset;
    int idx;

    s->Sclass = SCstatic;
    s->Sseg = cseg;		// identifier is defined in code segment
    s->Soffset = Coffset;

    // Store numparam into right places
    assert((numparam & 0xFFFF) == numparam);	// 2 byte value
    TOWORD(&cod32_2[32],numparam);
    TOWORD(&cod32_2[32 + 7],numparam);
    TOWORD(&cod32_3[sizeof(cod32_3) - 2],numparam);

    //------------------------------------------
    // Generate CODE16 segment if it isn't there already
    if (obj.code16 == 0)
    {
	// Define CODE16 segment for far16 thunks

	static char lname[] = { "\06CODE16" };
	unsigned attr;

	// Put out LNAMES record
	objrecord(LNAMES,lname,sizeof(lname) - 1);

	obj.code16segi = obj_newfarseg(0,4);
	obj.code16 = obj.segidx;			// code16 segment index
	obj.CODE16offset = 0;

	// class CODE
	attr = SEG_ATTR(SEG_ALIGN2,SEG_C_PUBLIC,0,USE16);
	SegData[obj.code16segi]->attr = attr;
	objsegdef(attr,0,obj.lnameidx++,4);
	obj.segidx++;
    }

    //------------------------------------------
    // Output the 32 bit thunk

    obj_bytes(cseg,Coffset,sizeof(cod32_1),cod32_1);
    Coffset += sizeof(cod32_1);

    // Put out fixup for SEG FLAT:_DATA
    objledata(cseg,Coffset,0,LOCATsegrel|LOCbase|FD_F1|FD_T4,
	DGROUPIDX,DATA);
    Coffset += 2;

    obj_bytes(cseg,Coffset,sizeof(cod32_2),cod32_2);
    Coffset += sizeof(cod32_2);

    // Put out fixup to CODE16 part of thunk
    objledata(cseg,Coffset,obj.CODE16offset,LOCATsegrel|LOC16pointer|FD_F0|FD_T4,
	obj.code16,obj.code16);
    Coffset += 4;

    L2offset = Coffset;
    obj_bytes(cseg,Coffset,sizeof(cod32_3),cod32_3);
    Coffset += sizeof(cod32_3);

    s->Ssize = Coffset - s->Soffset;		// size of thunk

    //------------------------------------------
    // Output the 16 bit thunk

    obj_byte(obj.code16,obj.CODE16offset++,0x9A);	//	CALLF	function

    // Make function external
    idx = objextern(s);				// use Pascal name mangling

    // Output fixup for function
    objledata(obj.code16,obj.CODE16offset,0,LOCATsegrel|LOC16pointer|FD_F2|FD_T6,
	idx,idx);
    obj.CODE16offset += 4;

    obj_bytes(obj.code16,obj.CODE16offset,3,"\x66\x67\xEA");	// JMPF	L2
    obj.CODE16offset += 3;

    objledata(obj.code16,obj.CODE16offset,L2offset,
	LOCATsegrel | LOC32pointer | FD_F1 | FD_T4,DGROUPIDX,cseg);
    obj.CODE16offset += 6;

    SegData[obj.code16segi]->SDoffset = obj.CODE16offset;
}

/**************************************
 * Mark object file as using floating point.
 */

void obj_fltused()
{
    if (!obj.fltused)
    {
	obj.fltused = 1;
	if (!(config.flags3 & CFG3wkfloat))
	    objextdef("__fltused");
    }
}


/****************************************
 * Find longest match of pattern[] in dict[].
 */

static int longest_match(char *dict, int dlen, char *pattern, int plen,
	int *pmatchoff, int *pmatchlen)
{
    int matchlen = 0;
    int matchoff;

    int i;
    int j;

    for (i = 0; i < dlen; i++)
    {
	if (dict[i] == pattern[0])
	{
	    for (j = 1; 1; j++)
	    {
		if (i + j == dlen || j == plen)
		    break;
		if (dict[i + j] != pattern[j])
		    break;
	    }
	    if (j >= matchlen)
	    {
		matchlen = j;
		matchoff = i;
	    }
	}
    }

    if (matchlen > 1)
    {
	*pmatchlen = matchlen;
	*pmatchoff = matchoff;
	return 1;			// found a match
    }
    return 0;				// no match
}

/******************************************
 * Compress an identifier.
 * Format: if ASCII, then it's just the char
 *	if high bit set, then it's a length/offset pair
 * Returns:
 *	malloc'd compressed identifier
 */

char *id_compress(char *id, int idlen)
{
    int i;
    int count = 0;
    char *p;

    p = (char *)malloc(idlen + 1);
    for (i = 0; i < idlen; i++)
    {
	int matchoff;
	int matchlen;

	int j = 0;
	if (i > 1023)
	    j = i - 1023;

	if (longest_match(id + j, i - j, id + i, idlen - i, &matchoff, &matchlen))
	{   int off;

	    matchoff += j;
	    off = i - matchoff;
	    //printf("matchoff = %3d, matchlen = %2d, off = %d\n", matchoff, matchlen, off);
	    assert(off >= matchlen);

	    if (off <= 8 && matchlen <= 8)
	    {
		p[count] = 0xC0 | ((off - 1) << 3) | (matchlen - 1);
		count++;
		i += matchlen - 1;
		continue;
	    }
	    else if (matchlen > 2 && off < 1024)
	    {
		if (matchlen >= 1024)
		    matchlen = 1023;	// longest representable match
		p[count + 0] = 0x80 | ((matchlen >> 4) & 0x38) | ((off >> 7) & 7);
		p[count + 1] = 0x80 | matchlen;
		p[count + 2] = 0x80 | off;
		count += 3;
		i += matchlen - 1;
		continue;
	    }
	}
	p[count] = id[i];
	count++;
    }
    p[count] = 0;
    //printf("old size = %d, new size = %d\n", idlen, count);
    return p;
}

#endif
#endif
